From 45dc2cbe2a1accda39f49118e7d72f06579a5323 Mon Sep 17 00:00:00 2001 From: Gabriel Bernal Date: Wed, 15 Jan 2025 13:17:26 +0100 Subject: [PATCH] add remaining plugins, refactor exports and add test and lint commands Signed-off-by: Gabriel Bernal --- .cjs.swcrc | 20 + .eslintrc.js | 2 +- .github/workflows/ci.yml | 33 +- BarChart/jest.config.ts | 23 + BarChart/package.json | 9 +- BarChart/rsbuild.config.ts | 3 +- BarChart/src/BarChart.ts | 32 + BarChart/src/BarChart.tsx | 120 - .../BarChartOptionsEditorSettings.test.tsx | 164 + .../src/BarChartOptionsEditorSettings.tsx | 109 + BarChart/src/{Chart.tsx => BarChartPanel.tsx} | 36 +- BarChart/src/bar-chart-model.ts | 51 + BarChart/src/index.ts | 15 + BarChart/src/setup-tests.ts | 17 + BarChart/src/utils.test.ts | 120 + BarChart/src/utils.ts | 20 +- BarChart/tsconfig.json | 9 +- GaugeChart/jest.config.ts | 23 + GaugeChart/package.json | 9 +- GaugeChart/rsbuild.config.ts | 3 +- GaugeChart/src/GaugeChart.ts | 32 + .../GaugeChartOptionsEditorSettings.test.tsx | 102 + .../src/GaugeChartOptionsEditorSettings.tsx | 107 + .../{GaugeChart.tsx => GaugeChartPanel.tsx} | 84 +- .../src/{model.ts => gauge-chart-model.ts} | 45 +- GaugeChart/src/setup-tests.ts | 17 + .../src/{tresholds.ts => thresholds.ts} | 13 + GaugeChart/tsconfig.json | 2 +- MarkdownChart/jest.config.ts | 23 + MarkdownChart/package.json | 10 +- MarkdownChart/rsbuild.config.ts | 2 +- MarkdownChart/src/MarkdownChart.ts | 33 + MarkdownChart/src/MarkdownChart.tsx | 69 - MarkdownChart/src/MarkdownPanel.tsx | 95 + .../src/MarkdownPanelOptionsEditor.tsx | 50 + MarkdownChart/src/markdown-panel-model.ts | 29 + MarkdownChart/src/setup-tests.ts | 17 + MarkdownChart/tsconfig.json | 2 +- PieChart/README.md | 23 + PieChart/jest.config.ts | 23 + PieChart/package.json | 47 + PieChart/rsbuild.config.ts | 59 + PieChart/src/PieChart.ts | 32 + .../src/PieChartOptionsEditorSettings.tsx | 118 + PieChart/src/PieChartPanel.tsx | 200 + PieChart/src/bootstrap.tsx | 18 + PieChart/src/env.d.ts | 14 + PieChart/src/index.tsx | 14 + PieChart/src/model.ts | 19 + PieChart/src/options-model.ts | 35 + PieChart/src/palette-gen.ts | 94 + PieChart/src/palette.ts | 66 + PieChart/src/pie-chart-model.ts | 107 + PieChart/src/setup-tests.ts | 17 + PieChart/src/utils.test.ts | 120 + PieChart/src/utils.ts | 58 + PieChart/tsconfig.json | 3 + Prometheus/jest.config.ts | 23 + Prometheus/package.json | 11 +- Prometheus/rsbuild.config.ts | 2 + .../src/plugins/prometheus-variables.tsx | 6 +- Prometheus/src/setup-tests.ts | 17 + ScatterChart/README.md | 23 + ScatterChart/jest.config.ts | 23 + ScatterChart/package.json | 49 + ScatterChart/rsbuild.config.ts | 59 + ScatterChart/src/ScatterChart.ts | 27 + ScatterChart/src/ScatterChartPanel.test.tsx | 117 + ScatterChart/src/ScatterChartPanel.tsx | 212 + ScatterChart/src/Scatterplot.tsx | 129 + ScatterChart/src/bootstrap.tsx | 18 + ScatterChart/src/env.d.ts | 14 + ScatterChart/src/get-trace-data-jaeger.ts | 157 + ScatterChart/src/index.tsx | 14 + ScatterChart/src/mock-trace-data.ts | 891 + ScatterChart/src/scatter-chart-model.ts | 35 + ScatterChart/src/setup-tests.ts | 17 + ScatterChart/tsconfig.json | 3 + StatChart/jest.config.ts | 23 + StatChart/package.json | 9 +- StatChart/rsbuild.config.ts | 2 +- StatChart/src/StatChart.ts | 32 + .../StatChartOptionsEditorSettings.test.tsx | 125 + .../src/StatChartOptionsEditorSettings.tsx | 102 + .../src/{StatChart.tsx => StatChartPanel.tsx} | 67 +- StatChart/src/model.ts | 2 + StatChart/src/setup-tests.ts | 17 + StatChart/src/stat-chart-model.ts | 48 + StatChart/src/utils/data-transform.test.ts | 86 + StatChart/src/{ => utils}/data-transform.ts | 4 +- StaticListVariable/package.json | 4 +- StatusHistoryChart/README.md | 23 + StatusHistoryChart/cue.mod/module.cue | 4 + StatusHistoryChart/jest.config.ts | 23 + StatusHistoryChart/package.json | 47 + StatusHistoryChart/rsbuild.config.ts | 61 + .../schemas/migrate/migrate.cue | 36 + StatusHistoryChart/schemas/status-history.cue | 31 + .../schemas/status-history.json | 8 + StatusHistoryChart/src/StatusHistoryChart.ts | 25 + ...tatusHistoryChartOptionsEditorSettings.tsx | 58 + StatusHistoryChart/src/StatusHistoryPanel.tsx | 85 + StatusHistoryChart/src/bootstrap.tsx | 18 + StatusHistoryChart/src/env.d.ts | 14 + StatusHistoryChart/src/index.tsx | 14 + StatusHistoryChart/src/setup-tests.ts | 17 + .../src/status-history-model.ts | 22 +- .../src/utils/data-transform.test.ts | 73 + .../src/utils/data-transform.ts | 101 + StatusHistoryChart/src/utils/get-timescale.ts | 22 + StatusHistoryChart/tsconfig.json | 3 + Table/README.md | 23 + Table/jest.config.ts | 23 + Table/package.json | 46 + Table/rsbuild.config.ts | 61 + Table/src/CellsEditor/CellEditor.tsx | 235 + Table/src/CellsEditor/CellsEditor.tsx | 78 + Table/src/CellsEditor/index.ts | 15 + Table/src/ColumnsEditor/ColumnEditor.tsx | 159 + .../ColumnsEditor/ColumnEditorContainer.tsx | 105 + Table/src/ColumnsEditor/ColumnsEditor.tsx | 89 + Table/src/ColumnsEditor/index.ts | 16 + Table/src/Table.ts | 38 + Table/src/TableCellsEditor.tsx | 27 + Table/src/TableColumnsEditor.test.tsx | 55 + Table/src/TableColumnsEditor.tsx | 27 + Table/src/TablePanel.test.tsx | 169 + Table/src/TablePanel.tsx | 252 + Table/src/TableSettingsEditor.tsx | 86 + Table/src/TableTransformsEditor.tsx | 28 + Table/src/bootstrap.tsx | 18 + Table/src/env.d.ts | 14 + Table/src/index.tsx | 14 + Table/src/model.ts | 5 + Table/src/setup-tests.ts | 17 + Table/src/table-model.ts | 122 + Table/src/test/mock-query-results.ts | 276 + Table/tsconfig.json | 3 + TimeSeriesChart/jest.config.ts | 23 + TimeSeriesChart/package.json | 9 +- TimeSeriesChart/rsbuild.config.ts | 2 +- TimeSeriesChart/src/QuerySettingsEditor.tsx | 214 + TimeSeriesChart/src/TimeChart.tsx | 458 - TimeSeriesChart/src/TimeSeriesChart.ts | 27 + .../TimeSeriesChartOptionsEditorSettings.tsx | 114 + .../src/TimeSeriesChartPanel.test.tsx | 215 + ...riesChart.tsx => TimeSeriesChartPanel.tsx} | 191 +- .../src/VisualOptionsEditor.test.tsx | 55 + TimeSeriesChart/src/VisualOptionsEditor.tsx | 163 + TimeSeriesChart/src/YAxisOptionsEditor.tsx | 104 + TimeSeriesChart/src/setup-tests.ts | 17 + .../src/test/mock-query-results.ts | 276 + .../{model.ts => time-series-chart-model.ts} | 142 +- .../src/utils/data-transform.test.ts | 117 + TimeSeriesChart/src/utils/data-transform.ts | 21 +- TimeSeriesChart/src/utils/palette-gen.test.ts | 235 + TimeSeriesChart/src/utils/palette-gen.ts | 89 +- TimeSeriesChart/src/utils/palette.ts | 66 + TimeSeriesTable/README.md | 23 + TimeSeriesTable/jest.config.ts | 23 + TimeSeriesTable/package.json | 46 + TimeSeriesTable/rsbuild.config.ts | 59 + TimeSeriesTable/src/DataTable.tsx | 130 + TimeSeriesTable/src/SeriesName.tsx | 93 + TimeSeriesTable/src/TimeSeriesTable.ts | 33 + .../src/TimeSeriesTablePanel.test.tsx | 141 + TimeSeriesTable/src/TimeSeriesTablePanel.tsx | 36 + TimeSeriesTable/src/bootstrap.tsx | 18 + TimeSeriesTable/src/env.d.ts | 14 + TimeSeriesTable/src/index.tsx | 14 + TimeSeriesTable/src/model.ts | 5 + TimeSeriesTable/src/setup-tests.ts | 17 + .../src/test/mock-query-results.ts | 276 + TimeSeriesTable/tsconfig.json | 3 + TraceTable/README.md | 23 + TraceTable/jest.config.ts | 23 + TraceTable/package.json | 48 + TraceTable/rsbuild.config.ts | 60 + TraceTable/src/DataTable.tsx | 235 + TraceTable/src/TraceTable.ts | 25 + TraceTable/src/TraceTablePanel.test.tsx | 110 + TraceTable/src/TraceTablePanel.tsx | 80 + TraceTable/src/bootstrap.tsx | 18 + TraceTable/src/env.d.ts | 14 + TraceTable/src/index.tsx | 14 + TraceTable/src/model.ts | 9 + TraceTable/src/setup-tests.ts | 17 + TraceTable/src/test/get-trace-data-jaeger.ts | 157 + TraceTable/src/test/mock-trace-data.ts | 885 + TraceTable/src/trace-table-model.ts | 35 + TraceTable/src/utils/palette.ts | 66 + TraceTable/src/utils/utils.ts | 69 + TraceTable/tsconfig.json | 3 + TracingGanttChart/README.md | 23 + TracingGanttChart/jest.config.ts | 23 + TracingGanttChart/package.json | 47 + TracingGanttChart/rsbuild.config.ts | 59 + TracingGanttChart/src/TracingGanttChart.ts | 22 + .../DetailPane/Attributes.test.tsx | 74 + .../DetailPane/Attributes.tsx | 80 + .../DetailPane/DetailPane.tsx | 67 + .../DetailPane/SpanEvents.test.tsx | 32 + .../DetailPane/SpanEvents.tsx | 61 + .../GanttTable/GanttTable.test.tsx | 64 + .../GanttTable/GanttTable.tsx | 100 + .../GanttTable/GanttTableHeader.tsx | 50 + .../GanttTable/GanttTableProvider.tsx | 58 + .../GanttTable/GanttTableRow.tsx | 63 + .../GanttTable/ResizableDivider.tsx | 98 + .../GanttTable/SpanDuration.test.tsx | 71 + .../GanttTable/SpanDuration.tsx | 86 + .../GanttTable/SpanIndents.tsx | 99 + .../GanttTable/SpanName.test.tsx | 40 + .../TracingGanttChart/GanttTable/SpanName.tsx | 41 + .../MiniGanttChart/Canvas.tsx | 219 + .../MiniGanttChart/MiniGanttChart.tsx | 49 + .../TracingGanttChart/MiniGanttChart/draw.ts | 66 + .../src/TracingGanttChart/Ticks.test.tsx | 35 + .../src/TracingGanttChart/Ticks.tsx | 64 + .../TracingGanttChart/TracingGanttChart.tsx | 88 + .../src/TracingGanttChart/palette.ts | 66 + .../src/TracingGanttChart/trace.test.ts | 29 + .../src/TracingGanttChart/trace.ts | 52 + .../src/TracingGanttChart/utils.test.ts | 29 + .../src/TracingGanttChart/utils.ts | 69 + .../src/TracingGanttChartPanel.tsx | 62 + TracingGanttChart/src/bootstrap.tsx | 18 + TracingGanttChart/src/env.d.ts | 14 + TracingGanttChart/src/gantt-chart-model.ts | 35 + TracingGanttChart/src/index.tsx | 14 + TracingGanttChart/src/setup-tests.ts | 17 + .../src/test/get-trace-data-jaeger.ts | 157 + TracingGanttChart/src/test/mock-trace-data.ts | 885 + TracingGanttChart/tsconfig.json | 15 + jest.shared.ts | 42 + package-lock.json | 15152 ++++++++++------ package.json | 27 +- scripts/build-archive/build-archive.go | 17 +- stylesMock.js | 14 + tsconfig.base.json | 3 +- 240 files changed, 25434 insertions(+), 6093 deletions(-) create mode 100644 .cjs.swcrc create mode 100644 BarChart/jest.config.ts create mode 100644 BarChart/src/BarChart.ts delete mode 100644 BarChart/src/BarChart.tsx create mode 100644 BarChart/src/BarChartOptionsEditorSettings.test.tsx create mode 100644 BarChart/src/BarChartOptionsEditorSettings.tsx rename BarChart/src/{Chart.tsx => BarChartPanel.tsx} (70%) create mode 100644 BarChart/src/bar-chart-model.ts create mode 100644 BarChart/src/index.ts create mode 100644 BarChart/src/setup-tests.ts create mode 100644 BarChart/src/utils.test.ts create mode 100644 GaugeChart/jest.config.ts create mode 100644 GaugeChart/src/GaugeChart.ts create mode 100644 GaugeChart/src/GaugeChartOptionsEditorSettings.test.tsx create mode 100644 GaugeChart/src/GaugeChartOptionsEditorSettings.tsx rename GaugeChart/src/{GaugeChart.tsx => GaugeChartPanel.tsx} (63%) rename GaugeChart/src/{model.ts => gauge-chart-model.ts} (50%) create mode 100644 GaugeChart/src/setup-tests.ts rename GaugeChart/src/{tresholds.ts => thresholds.ts} (72%) create mode 100644 MarkdownChart/jest.config.ts create mode 100644 MarkdownChart/src/MarkdownChart.ts delete mode 100644 MarkdownChart/src/MarkdownChart.tsx create mode 100644 MarkdownChart/src/MarkdownPanel.tsx create mode 100644 MarkdownChart/src/MarkdownPanelOptionsEditor.tsx create mode 100644 MarkdownChart/src/markdown-panel-model.ts create mode 100644 MarkdownChart/src/setup-tests.ts create mode 100644 PieChart/README.md create mode 100644 PieChart/jest.config.ts create mode 100644 PieChart/package.json create mode 100644 PieChart/rsbuild.config.ts create mode 100644 PieChart/src/PieChart.ts create mode 100644 PieChart/src/PieChartOptionsEditorSettings.tsx create mode 100644 PieChart/src/PieChartPanel.tsx create mode 100644 PieChart/src/bootstrap.tsx create mode 100644 PieChart/src/env.d.ts create mode 100644 PieChart/src/index.tsx create mode 100644 PieChart/src/model.ts create mode 100644 PieChart/src/options-model.ts create mode 100644 PieChart/src/palette-gen.ts create mode 100644 PieChart/src/palette.ts create mode 100644 PieChart/src/pie-chart-model.ts create mode 100644 PieChart/src/setup-tests.ts create mode 100644 PieChart/src/utils.test.ts create mode 100644 PieChart/src/utils.ts create mode 100644 PieChart/tsconfig.json create mode 100644 Prometheus/jest.config.ts create mode 100644 Prometheus/src/setup-tests.ts create mode 100644 ScatterChart/README.md create mode 100644 ScatterChart/jest.config.ts create mode 100644 ScatterChart/package.json create mode 100644 ScatterChart/rsbuild.config.ts create mode 100644 ScatterChart/src/ScatterChart.ts create mode 100644 ScatterChart/src/ScatterChartPanel.test.tsx create mode 100644 ScatterChart/src/ScatterChartPanel.tsx create mode 100644 ScatterChart/src/Scatterplot.tsx create mode 100644 ScatterChart/src/bootstrap.tsx create mode 100644 ScatterChart/src/env.d.ts create mode 100644 ScatterChart/src/get-trace-data-jaeger.ts create mode 100644 ScatterChart/src/index.tsx create mode 100644 ScatterChart/src/mock-trace-data.ts create mode 100644 ScatterChart/src/scatter-chart-model.ts create mode 100644 ScatterChart/src/setup-tests.ts create mode 100644 ScatterChart/tsconfig.json create mode 100644 StatChart/jest.config.ts create mode 100644 StatChart/src/StatChart.ts create mode 100644 StatChart/src/StatChartOptionsEditorSettings.test.tsx create mode 100644 StatChart/src/StatChartOptionsEditorSettings.tsx rename StatChart/src/{StatChart.tsx => StatChartPanel.tsx} (68%) create mode 100644 StatChart/src/setup-tests.ts create mode 100644 StatChart/src/stat-chart-model.ts create mode 100644 StatChart/src/utils/data-transform.test.ts rename StatChart/src/{ => utils}/data-transform.ts (96%) create mode 100644 StatusHistoryChart/README.md create mode 100644 StatusHistoryChart/cue.mod/module.cue create mode 100644 StatusHistoryChart/jest.config.ts create mode 100644 StatusHistoryChart/package.json create mode 100644 StatusHistoryChart/rsbuild.config.ts create mode 100644 StatusHistoryChart/schemas/migrate/migrate.cue create mode 100644 StatusHistoryChart/schemas/status-history.cue create mode 100644 StatusHistoryChart/schemas/status-history.json create mode 100644 StatusHistoryChart/src/StatusHistoryChart.ts create mode 100644 StatusHistoryChart/src/StatusHistoryChartOptionsEditorSettings.tsx create mode 100644 StatusHistoryChart/src/StatusHistoryPanel.tsx create mode 100644 StatusHistoryChart/src/bootstrap.tsx create mode 100644 StatusHistoryChart/src/env.d.ts create mode 100644 StatusHistoryChart/src/index.tsx create mode 100644 StatusHistoryChart/src/setup-tests.ts rename BarChart/src/model.ts => StatusHistoryChart/src/status-history-model.ts (56%) create mode 100644 StatusHistoryChart/src/utils/data-transform.test.ts create mode 100644 StatusHistoryChart/src/utils/data-transform.ts create mode 100644 StatusHistoryChart/src/utils/get-timescale.ts create mode 100644 StatusHistoryChart/tsconfig.json create mode 100644 Table/README.md create mode 100644 Table/jest.config.ts create mode 100644 Table/package.json create mode 100644 Table/rsbuild.config.ts create mode 100644 Table/src/CellsEditor/CellEditor.tsx create mode 100644 Table/src/CellsEditor/CellsEditor.tsx create mode 100644 Table/src/CellsEditor/index.ts create mode 100644 Table/src/ColumnsEditor/ColumnEditor.tsx create mode 100644 Table/src/ColumnsEditor/ColumnEditorContainer.tsx create mode 100644 Table/src/ColumnsEditor/ColumnsEditor.tsx create mode 100644 Table/src/ColumnsEditor/index.ts create mode 100644 Table/src/Table.ts create mode 100644 Table/src/TableCellsEditor.tsx create mode 100644 Table/src/TableColumnsEditor.test.tsx create mode 100644 Table/src/TableColumnsEditor.tsx create mode 100644 Table/src/TablePanel.test.tsx create mode 100644 Table/src/TablePanel.tsx create mode 100644 Table/src/TableSettingsEditor.tsx create mode 100644 Table/src/TableTransformsEditor.tsx create mode 100644 Table/src/bootstrap.tsx create mode 100644 Table/src/env.d.ts create mode 100644 Table/src/index.tsx create mode 100644 Table/src/model.ts create mode 100644 Table/src/setup-tests.ts create mode 100644 Table/src/table-model.ts create mode 100644 Table/src/test/mock-query-results.ts create mode 100644 Table/tsconfig.json create mode 100644 TimeSeriesChart/jest.config.ts create mode 100644 TimeSeriesChart/src/QuerySettingsEditor.tsx delete mode 100644 TimeSeriesChart/src/TimeChart.tsx create mode 100644 TimeSeriesChart/src/TimeSeriesChart.ts create mode 100644 TimeSeriesChart/src/TimeSeriesChartOptionsEditorSettings.tsx create mode 100644 TimeSeriesChart/src/TimeSeriesChartPanel.test.tsx rename TimeSeriesChart/src/{TimeSeriesChart.tsx => TimeSeriesChartPanel.tsx} (80%) create mode 100644 TimeSeriesChart/src/VisualOptionsEditor.test.tsx create mode 100644 TimeSeriesChart/src/VisualOptionsEditor.tsx create mode 100644 TimeSeriesChart/src/YAxisOptionsEditor.tsx create mode 100644 TimeSeriesChart/src/setup-tests.ts create mode 100644 TimeSeriesChart/src/test/mock-query-results.ts rename TimeSeriesChart/src/{model.ts => time-series-chart-model.ts} (54%) create mode 100644 TimeSeriesChart/src/utils/data-transform.test.ts create mode 100644 TimeSeriesChart/src/utils/palette-gen.test.ts create mode 100644 TimeSeriesChart/src/utils/palette.ts create mode 100644 TimeSeriesTable/README.md create mode 100644 TimeSeriesTable/jest.config.ts create mode 100644 TimeSeriesTable/package.json create mode 100644 TimeSeriesTable/rsbuild.config.ts create mode 100644 TimeSeriesTable/src/DataTable.tsx create mode 100644 TimeSeriesTable/src/SeriesName.tsx create mode 100644 TimeSeriesTable/src/TimeSeriesTable.ts create mode 100644 TimeSeriesTable/src/TimeSeriesTablePanel.test.tsx create mode 100644 TimeSeriesTable/src/TimeSeriesTablePanel.tsx create mode 100644 TimeSeriesTable/src/bootstrap.tsx create mode 100644 TimeSeriesTable/src/env.d.ts create mode 100644 TimeSeriesTable/src/index.tsx create mode 100644 TimeSeriesTable/src/model.ts create mode 100644 TimeSeriesTable/src/setup-tests.ts create mode 100644 TimeSeriesTable/src/test/mock-query-results.ts create mode 100644 TimeSeriesTable/tsconfig.json create mode 100644 TraceTable/README.md create mode 100644 TraceTable/jest.config.ts create mode 100644 TraceTable/package.json create mode 100644 TraceTable/rsbuild.config.ts create mode 100644 TraceTable/src/DataTable.tsx create mode 100644 TraceTable/src/TraceTable.ts create mode 100644 TraceTable/src/TraceTablePanel.test.tsx create mode 100644 TraceTable/src/TraceTablePanel.tsx create mode 100644 TraceTable/src/bootstrap.tsx create mode 100644 TraceTable/src/env.d.ts create mode 100644 TraceTable/src/index.tsx create mode 100644 TraceTable/src/model.ts create mode 100644 TraceTable/src/setup-tests.ts create mode 100644 TraceTable/src/test/get-trace-data-jaeger.ts create mode 100644 TraceTable/src/test/mock-trace-data.ts create mode 100644 TraceTable/src/trace-table-model.ts create mode 100644 TraceTable/src/utils/palette.ts create mode 100644 TraceTable/src/utils/utils.ts create mode 100644 TraceTable/tsconfig.json create mode 100644 TracingGanttChart/README.md create mode 100644 TracingGanttChart/jest.config.ts create mode 100644 TracingGanttChart/package.json create mode 100644 TracingGanttChart/rsbuild.config.ts create mode 100644 TracingGanttChart/src/TracingGanttChart.ts create mode 100644 TracingGanttChart/src/TracingGanttChart/DetailPane/Attributes.test.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/DetailPane/Attributes.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/DetailPane/DetailPane.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/DetailPane/SpanEvents.test.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/DetailPane/SpanEvents.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTable.test.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTable.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableHeader.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableProvider.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableRow.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/GanttTable/ResizableDivider.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/GanttTable/SpanDuration.test.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/GanttTable/SpanDuration.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/GanttTable/SpanIndents.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/GanttTable/SpanName.test.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/GanttTable/SpanName.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/MiniGanttChart/Canvas.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/MiniGanttChart/MiniGanttChart.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/MiniGanttChart/draw.ts create mode 100644 TracingGanttChart/src/TracingGanttChart/Ticks.test.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/Ticks.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/TracingGanttChart.tsx create mode 100644 TracingGanttChart/src/TracingGanttChart/palette.ts create mode 100644 TracingGanttChart/src/TracingGanttChart/trace.test.ts create mode 100644 TracingGanttChart/src/TracingGanttChart/trace.ts create mode 100644 TracingGanttChart/src/TracingGanttChart/utils.test.ts create mode 100644 TracingGanttChart/src/TracingGanttChart/utils.ts create mode 100644 TracingGanttChart/src/TracingGanttChartPanel.tsx create mode 100644 TracingGanttChart/src/bootstrap.tsx create mode 100644 TracingGanttChart/src/env.d.ts create mode 100644 TracingGanttChart/src/gantt-chart-model.ts create mode 100644 TracingGanttChart/src/index.tsx create mode 100644 TracingGanttChart/src/setup-tests.ts create mode 100644 TracingGanttChart/src/test/get-trace-data-jaeger.ts create mode 100644 TracingGanttChart/src/test/mock-trace-data.ts create mode 100644 TracingGanttChart/tsconfig.json create mode 100644 jest.shared.ts create mode 100644 stylesMock.js diff --git a/.cjs.swcrc b/.cjs.swcrc new file mode 100644 index 0000000..948b731 --- /dev/null +++ b/.cjs.swcrc @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": true + }, + "target": "es2018", + "transform": { + "react": { + "runtime": "automatic", + "useBuiltins": true + } + } + }, + "module": { + "type": "commonjs" + }, + "exclude": ["\\.(stories|test)\\."] +} diff --git a/.eslintrc.js b/.eslintrc.js index bd8bfba..9436480 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -60,7 +60,7 @@ module.exports = { 'import/order': 'error', // you must disable the base rule as it can report incorrect errors 'no-unused-vars': 'off', - '@typescript-eslint/no-unused-vars': ['error'], + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], 'react/prop-types': 'off', 'react-hooks/exhaustive-deps': 'error', diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 246d322..6944683 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ on: jobs: get-schemas-deps: - name: "Get dependencies for plugin schemas" + name: 'Get dependencies for plugin schemas' runs-on: ubuntu-latest steps: - name: checkout @@ -22,7 +22,7 @@ jobs: enable_go: true - uses: ./.github/perses-ci/actions/install_percli with: - cli_version: "v0.50.0-rc.0" + cli_version: 'v0.50.0-rc.0' - run: go run ./scripts/get-schemas-deps/get-schemas-deps.go - name: store plugin schema dependencies uses: actions/upload-artifact@v4 @@ -32,8 +32,8 @@ jobs: */cue.mod/pkg build: - name: "build" - needs: "get-schemas-deps" + name: 'build' + needs: 'get-schemas-deps' runs-on: ubuntu-latest steps: - name: checkout @@ -59,7 +59,7 @@ jobs: !node_modules lint: - name: "lint" + name: 'lint' runs-on: ubuntu-latest steps: - name: checkout @@ -71,9 +71,22 @@ jobs: - run: npm ci - run: npm run lint + test: + name: 'test' + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v4 + - uses: perses/github-actions@v0.5.2 + - uses: ./.github/perses-ci/actions/setup_environment + with: + enable_npm: true + - run: npm ci + - run: npm run test + validate-schemas: - name: "Validate plugin schemas" - needs: "get-schemas-deps" + name: 'Validate plugin schemas' + needs: 'get-schemas-deps' runs-on: ubuntu-latest steps: - name: checkout @@ -82,7 +95,7 @@ jobs: - uses: ./.github/perses-ci/actions/setup_environment with: enable_go: true - cue_version: "v0.11.0" + cue_version: 'v0.11.0' enable_cue: true - name: retrieve plugin schema dependencies uses: actions/download-artifact@v4 @@ -91,8 +104,8 @@ jobs: - run: go run ./scripts/validate-schemas/validate-schemas.go release: - name: "release" - needs: "build" + name: 'release' + needs: 'build' runs-on: ubuntu-latest permissions: contents: write diff --git a/BarChart/jest.config.ts b/BarChart/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/BarChart/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/BarChart/package.json b/BarChart/package.json index ea14e7b..6aa33c1 100644 --- a/BarChart/package.json +++ b/BarChart/package.json @@ -1,11 +1,13 @@ { "name": "@perses-dev/bar-chart", "private": true, - "version": "0.3.1", + "version": "0.4.0", "scripts": { "dev": "rsbuild dev", "build": "rsbuild build", - "lint": "eslint src --ext .ts,.tsx" + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" }, "dependencies": { "@module-federation/enhanced": "^0.1.11" @@ -23,7 +25,8 @@ "lodash": "^4.17.21", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0", - "use-resize-observer": "^9.0.0" + "use-resize-observer": "^9.0.0", + "immer": "^9.0.15" }, "files": [ "dist" diff --git a/BarChart/rsbuild.config.ts b/BarChart/rsbuild.config.ts index 1c6fe18..5a2172b 100644 --- a/BarChart/rsbuild.config.ts +++ b/BarChart/rsbuild.config.ts @@ -35,7 +35,7 @@ export default defineConfig({ new ModuleFederationPlugin({ name: 'BarChart', exposes: { - './BarChart': './src/Chart.tsx', + './BarChart': './src/BarChart.ts', }, shared: { react: { requiredVersion: '^18.2.0', singleton: true }, @@ -51,6 +51,7 @@ export default defineConfig({ '@hookform/resolvers': { singleton: true }, 'use-resize-observer': { requiredVersion: '^9.1.0', singleton: true }, 'mdi-material-ui': { requiredVersion: '^7.4.0', singleton: true }, + immer: { singleton: true }, }, dts: false, runtime: false, diff --git a/BarChart/src/BarChart.ts b/BarChart/src/BarChart.ts new file mode 100644 index 0000000..ea7e7f8 --- /dev/null +++ b/BarChart/src/BarChart.ts @@ -0,0 +1,32 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin } from '@perses-dev/plugin-system'; +import { createInitialBarChartOptions, BarChartOptions } from './bar-chart-model'; +import { BarChartOptionsEditorSettings } from './BarChartOptionsEditorSettings'; +import { BarChartPanel } from './BarChartPanel'; + +/** + * The core BarChart panel plugin for Perses. + */ +export const BarChart: PanelPlugin = { + PanelComponent: BarChartPanel, + panelOptionsEditorComponents: [ + { + label: 'Settings', + content: BarChartOptionsEditorSettings, + }, + ], + supportedQueryTypes: ['TimeSeriesQuery'], + createInitialOptions: createInitialBarChartOptions, +}; diff --git a/BarChart/src/BarChart.tsx b/BarChart/src/BarChart.tsx deleted file mode 100644 index 12dbf7c..0000000 --- a/BarChart/src/BarChart.tsx +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2023 The Perses Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { EChart, getFormattedAxis, useChartsTheme } from '@perses-dev/components'; -import { FormatOptions, formatValue } from '@perses-dev/core'; -import { BarChart as EChartsBarChart } from 'echarts/charts'; -import { DatasetComponent, GridComponent, TitleComponent, TooltipComponent } from 'echarts/components'; -import { EChartsCoreOption, use } from 'echarts/core'; -import { CanvasRenderer } from 'echarts/renderers'; -import { useMemo } from 'react'; -import { BarChartData, ModeOption } from './model'; - -use([EChartsBarChart, GridComponent, DatasetComponent, TitleComponent, TooltipComponent, CanvasRenderer]); - -const BAR_WIN_WIDTH = 14; -const BAR_GAP = 6; - -export interface BarChartProps { - width: number; - height: number; - data: BarChartData[] | null; - format?: FormatOptions; - mode?: ModeOption; -} - -export function BarChart(props: BarChartProps) { - const { width, height, data, format = { unit: 'decimal' }, mode = 'value' } = props; - const chartsTheme = useChartsTheme(); - - const option: EChartsCoreOption = useMemo(() => { - if (!data || !data.length) return chartsTheme.noDataOption; - - const source: Array> = []; - data.map((d) => { - source.push([d.label, d.value]); - }); - - return { - title: { - show: false, - }, - dataset: [ - { - dimensions: ['label', 'value'], - source: source, - }, - ], - xAxis: getFormattedAxis({}, format), - yAxis: { - type: 'category', - splitLine: { - show: false, - }, - axisLabel: { - overflow: 'truncate', - width: width / 3, - }, - }, - series: { - type: 'bar', - label: { - show: true, - position: 'right', - formatter: (params: { data: number[] }) => { - if (mode === 'percentage') { - return ( - params.data[1] && - formatValue(params.data[1], { - unit: 'percent', - decimalPlaces: format.decimalPlaces, - }) - ); - } - return params.data[1] && formatValue(params.data[1], format); - }, - barMinWidth: BAR_WIN_WIDTH, - barCategoryGap: BAR_GAP, - }, - itemStyle: { - borderRadius: 4, - color: chartsTheme.echartsTheme[0], - }, - }, - tooltip: { - appendToBody: true, - confine: true, - formatter: (params: { name: string; data: number[] }) => - params.data[1] && `${params.name}   ${formatValue(params.data[1], format)}`, - }, - // increase distance between grid and container to prevent y axis labels from getting cut off - grid: { - left: '5%', - right: '5%', - }, - }; - }, [data, chartsTheme, width, mode, format]); - - return ( -
- -
- ); -} diff --git a/BarChart/src/BarChartOptionsEditorSettings.test.tsx b/BarChart/src/BarChartOptionsEditorSettings.test.tsx new file mode 100644 index 0000000..a9ddedd --- /dev/null +++ b/BarChart/src/BarChartOptionsEditorSettings.test.tsx @@ -0,0 +1,164 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ChartsProvider, testChartsTheme } from '@perses-dev/components'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { BarChartOptions } from './bar-chart-model'; +import { BarChartOptionsEditorSettings } from './BarChartOptionsEditorSettings'; + +describe('BarChartOptionsEditorSettings', () => { + const renderBarChartOptionsEditorSettings = (value?: BarChartOptions, onChange = jest.fn()): void => { + render( + + + + ); + }; + + it('can modify unit', () => { + const onChange = jest.fn(); + renderBarChartOptionsEditorSettings(undefined, onChange); + const unitSelector = screen.getByRole('combobox', { name: 'Unit' }); + userEvent.click(unitSelector); + const yearOption = screen.getByRole('option', { + name: 'Years', + }); + userEvent.click(yearOption); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + format: { + unit: 'years', + }, + }) + ); + }); + + it('can modify calculation', () => { + const onChange = jest.fn(); + renderBarChartOptionsEditorSettings(undefined, onChange); + const calcSelector = screen.getByRole('combobox', { name: 'Calculation' }); + userEvent.click(calcSelector); + const meanOption = screen.getByRole('option', { + name: /Average/, + }); + userEvent.click(meanOption); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + calculation: 'mean', + }) + ); + }); + + it('can modify sort order', () => { + const onChange = jest.fn(); + renderBarChartOptionsEditorSettings(undefined, onChange); + const sortSelector = screen.getByRole('combobox', { name: 'Sort' }); + userEvent.click(sortSelector); + const ascendingOption = screen.getByRole('option', { + name: /Ascending/, + }); + userEvent.click(ascendingOption); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + sort: 'asc', + }) + ); + }); + + it('can modify mode', () => { + const onChange = jest.fn(); + renderBarChartOptionsEditorSettings(undefined, onChange); + const modeSelector = screen.getByRole('combobox', { name: 'Mode' }); + userEvent.click(modeSelector); + const percentageOption = screen.getByRole('option', { + name: /Percentage/, + }); + userEvent.click(percentageOption); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + mode: 'percentage', + }) + ); + }); + + it('percentage mode is disabled when unit is percent', () => { + renderBarChartOptionsEditorSettings({ + format: { + unit: 'percent', + }, + calculation: 'first', + sort: 'desc', + mode: 'value', + }); + const modeSelector = screen.getByRole('combobox', { name: 'Mode' }); + userEvent.click(modeSelector); + const percentageOption = screen.getByRole('option', { + name: /Percentage/, + }); + expect(percentageOption).toHaveAttribute('aria-disabled', 'true'); + }); + + it('unit selector is disabled when mode is percentage', () => { + renderBarChartOptionsEditorSettings({ + format: { + unit: 'decimal', + }, + calculation: 'first', + sort: 'desc', + mode: 'percentage', + }); + const unitSelector = screen.getByRole('combobox', { name: 'Unit' }); + expect(unitSelector).toBeDisabled; + }); + + it('should reset settings to defaults', () => { + const onChange = jest.fn(); + renderBarChartOptionsEditorSettings( + { + format: { + unit: 'years', + }, + calculation: 'first', + sort: 'asc', + mode: 'percentage', + }, + onChange + ); + const resetButton = screen.getByRole('button', { name: 'Reset To Defaults' }); + userEvent.click(resetButton); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + format: { + unit: 'decimal', + shortValues: true, + }, + calculation: 'last', + sort: 'desc', + mode: 'value', + }) + ); + }); +}); diff --git a/BarChart/src/BarChartOptionsEditorSettings.tsx b/BarChart/src/BarChartOptionsEditorSettings.tsx new file mode 100644 index 0000000..7dc5b9f --- /dev/null +++ b/BarChart/src/BarChartOptionsEditorSettings.tsx @@ -0,0 +1,109 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Button } from '@mui/material'; +import { + FormatControls, + FormatControlsProps, + ModeOption, + ModeSelector, + ModeSelectorProps, + OptionsEditorColumn, + OptionsEditorGrid, + OptionsEditorGroup, + SortOption, + SortSelector, + SortSelectorProps, +} from '@perses-dev/components'; +import { CalculationType, DEFAULT_CALCULATION, FormatOptions, isPercentUnit } from '@perses-dev/core'; +import { CalculationSelector, CalculationSelectorProps } from '@perses-dev/plugin-system'; +import { produce } from 'immer'; +import merge from 'lodash/merge'; +import { MouseEventHandler, ReactElement } from 'react'; +import { + BarChartOptions, + BarChartOptionsEditorProps, + DEFAULT_FORMAT, + DEFAULT_MODE, + DEFAULT_SORT, +} from './bar-chart-model'; + +export function BarChartOptionsEditorSettings(props: BarChartOptionsEditorProps): ReactElement { + const { onChange, value } = props; + + const handleCalculationChange: CalculationSelectorProps['onChange'] = (newCalculation: CalculationType) => { + onChange( + produce(value, (draft: BarChartOptions) => { + draft.calculation = newCalculation; + }) + ); + }; + + const handleUnitChange: FormatControlsProps['onChange'] = (newFormat: FormatOptions) => { + onChange( + produce(value, (draft: BarChartOptions) => { + draft.format = newFormat; + }) + ); + }; + + const handleSortChange: SortSelectorProps['onChange'] = (newSort: SortOption) => { + onChange( + produce(value, (draft: BarChartOptions) => { + draft.sort = newSort; + }) + ); + }; + + const handleModeChange: ModeSelectorProps['onChange'] = (newMode: ModeOption) => { + onChange( + produce(value, (draft: BarChartOptions) => { + draft.mode = newMode; + }) + ); + }; + + const handleResetSettings: MouseEventHandler = () => { + onChange( + produce(value, (draft: BarChartOptions) => { + draft.calculation = DEFAULT_CALCULATION; + draft.format = DEFAULT_FORMAT; + draft.sort = DEFAULT_SORT; + draft.mode = DEFAULT_MODE; + }) + ); + }; + + // ensures decimalPlaces defaults to correct value + const format = merge({}, DEFAULT_FORMAT, value.format); + + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/BarChart/src/Chart.tsx b/BarChart/src/BarChartPanel.tsx similarity index 70% rename from BarChart/src/Chart.tsx rename to BarChart/src/BarChartPanel.tsx index 54a4548..1265c78 100644 --- a/BarChart/src/Chart.tsx +++ b/BarChart/src/BarChartPanel.tsx @@ -1,4 +1,4 @@ -// Copyright 2024 The Perses Authors +// Copyright 2023 The Perses Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -11,29 +11,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* eslint-disable @typescript-eslint/ban-ts-comment */ -import { useChartsTheme } from '@perses-dev/components'; -import { CalculationsMap, CalculationType, PanelDefinition } from '@perses-dev/core'; -import { useDataQueries } from '@perses-dev/plugin-system'; -import { useMemo } from 'react'; -import { BarChart } from './BarChart'; -import { BarChartData, BarChartOptions } from './model'; +import { BarChart, BarChartData, LoadingOverlay, useChartsTheme } from '@perses-dev/components'; +import { Box } from '@mui/material'; +import { ReactElement, useMemo } from 'react'; +import { CalculationType, CalculationsMap } from '@perses-dev/core'; +import { useDataQueries, PanelProps } from '@perses-dev/plugin-system'; +import { BarChartOptions } from './bar-chart-model'; import { calculatePercentages, sortSeriesData } from './utils'; -interface ChartProps { - definition: PanelDefinition; - contentDimensions?: { width: number; height: number }; -} +export type BarChartPanelProps = PanelProps; -export default function Chart(props: ChartProps) { +export function BarChartPanel(props: BarChartPanelProps): ReactElement | null { const { - definition: { - spec: { - plugin: { - spec: { calculation, format, sort, mode }, - }, - }, - }, + spec: { calculation, format, sort, mode }, contentDimensions, } = props; @@ -71,11 +61,11 @@ export default function Chart(props: ChartProps) { if (contentDimensions === undefined) return null; if (isLoading || isFetching) { - return
Loading...
; + return ; } return ( -
+ -
+ ); } diff --git a/BarChart/src/bar-chart-model.ts b/BarChart/src/bar-chart-model.ts new file mode 100644 index 0000000..dd011bb --- /dev/null +++ b/BarChart/src/bar-chart-model.ts @@ -0,0 +1,51 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ModeOption, SortOption } from '@perses-dev/components'; +import { CalculationType, DEFAULT_CALCULATION, Definition, FormatOptions } from '@perses-dev/core'; +import { OptionsEditorProps } from '@perses-dev/plugin-system'; + +export const DEFAULT_FORMAT: FormatOptions = { unit: 'decimal', shortValues: true }; +export const DEFAULT_SORT: SortOption = 'desc'; +export const DEFAULT_MODE: ModeOption = 'value'; + +/** + * The schema for a BarChart panel. + */ +export interface BarChartDefinition extends Definition { + kind: 'BarChart'; +} + +/** + * The Options object type supported by the BarChart panel plugin. + */ +export interface BarChartOptions { + calculation: CalculationType; + format?: FormatOptions; + sort?: SortOption; + mode?: ModeOption; +} + +export type BarChartOptionsEditorProps = OptionsEditorProps; + +/** + * Creates the initial/empty options for a BarChart panel. + */ +export function createInitialBarChartOptions(): BarChartOptions { + return { + calculation: DEFAULT_CALCULATION, + format: DEFAULT_FORMAT, + sort: DEFAULT_SORT, + mode: DEFAULT_MODE, + }; +} diff --git a/BarChart/src/index.ts b/BarChart/src/index.ts new file mode 100644 index 0000000..57eb883 --- /dev/null +++ b/BarChart/src/index.ts @@ -0,0 +1,15 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export * from './BarChart'; +export type { BarChartDefinition, BarChartOptions } from './bar-chart-model'; diff --git a/BarChart/src/setup-tests.ts b/BarChart/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/BarChart/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/BarChart/src/utils.test.ts b/BarChart/src/utils.test.ts new file mode 100644 index 0000000..452ca66 --- /dev/null +++ b/BarChart/src/utils.test.ts @@ -0,0 +1,120 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { BarChartData } from '@perses-dev/components'; +import { calculatePercentages, sortSeriesData } from './utils'; + +const MOCK_DATA: BarChartData[] = [ + { + label: 'Label 1', + value: 3, + }, + { + label: 'Label 2', + value: 2, + }, + { + label: 'Label 3', + value: 0, + }, + { + label: 'Label 4', + value: 5, + }, + { + label: 'Label 5', + value: null, + }, +]; + +describe('calculatePercentages', () => { + it('calculates correct percentage values', () => { + const percentages = calculatePercentages(MOCK_DATA); + expect(percentages).toEqual([ + { + label: 'Label 1', + value: 30, + }, + { + label: 'Label 2', + value: 20, + }, + { + label: 'Label 3', + value: 0, + }, + { + label: 'Label 4', + value: 50, + }, + { + label: 'Label 5', + value: 0, + }, + ]); + }); +}); + +describe('sortSeriesData', () => { + it('sorts in ascending order', () => { + const sorted = sortSeriesData(MOCK_DATA, 'asc'); + expect(sorted).toEqual([ + { + label: 'Label 4', + value: 5, + }, + { + label: 'Label 1', + value: 3, + }, + { + label: 'Label 2', + value: 2, + }, + { + label: 'Label 3', + value: 0, + }, + { + label: 'Label 5', + value: null, + }, + ]); + }); + + it('sorts in descending order', () => { + const sorted = sortSeriesData(MOCK_DATA, 'desc'); + expect(sorted).toEqual([ + { + label: 'Label 5', + value: null, + }, + { + label: 'Label 3', + value: 0, + }, + { + label: 'Label 2', + value: 2, + }, + { + label: 'Label 1', + value: 3, + }, + { + label: 'Label 4', + value: 5, + }, + ]); + }); +}); diff --git a/BarChart/src/utils.ts b/BarChart/src/utils.ts index ceec81d..7d63f8a 100644 --- a/BarChart/src/utils.ts +++ b/BarChart/src/utils.ts @@ -1,6 +1,20 @@ -import { BarChartData, DEFAULT_SORT, SortOption } from './model'; +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -export function calculatePercentages(data: BarChartData[]) { +import { BarChartData, SortOption } from '@perses-dev/components'; +import { DEFAULT_SORT } from './bar-chart-model'; + +export function calculatePercentages(data: BarChartData[]): Array<{ label: string; value: number }> { const sum = data.reduce((accumulator, { value }) => accumulator + (value ?? 0), 0); return data.map((seriesData) => { const percentage = ((seriesData.value ?? 0) / sum) * 100; @@ -11,7 +25,7 @@ export function calculatePercentages(data: BarChartData[]) { }); } -export function sortSeriesData(data: BarChartData[], sortOrder: SortOption = DEFAULT_SORT) { +export function sortSeriesData(data: BarChartData[], sortOrder: SortOption = DEFAULT_SORT): BarChartData[] { if (sortOrder === 'asc') { // sort in ascending order by value return data.sort((a, b) => { diff --git a/BarChart/tsconfig.json b/BarChart/tsconfig.json index da01ddb..806aa79 100644 --- a/BarChart/tsconfig.json +++ b/BarChart/tsconfig.json @@ -1,3 +1,8 @@ { - "extends": "../tsconfig.base.json" -} \ No newline at end of file + "extends": "../tsconfig.base.json", + "include": ["src"], + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + } +} diff --git a/GaugeChart/jest.config.ts b/GaugeChart/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/GaugeChart/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/GaugeChart/package.json b/GaugeChart/package.json index 1b6a3c9..8576a55 100644 --- a/GaugeChart/package.json +++ b/GaugeChart/package.json @@ -1,11 +1,13 @@ { "name": "@perses-dev/gauge-chart", "private": true, - "version": "0.3.0", + "version": "0.4.0", "scripts": { "dev": "rsbuild dev", "build": "rsbuild build", - "lint": "eslint src --ext .ts,.tsx" + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" }, "dependencies": { "@module-federation/enhanced": "^0.1.11" @@ -23,7 +25,8 @@ "lodash": "^4.17.21", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0", - "use-resize-observer": "^9.0.0" + "use-resize-observer": "^9.0.0", + "immer": "^9.0.15" }, "files": [ "dist" diff --git a/GaugeChart/rsbuild.config.ts b/GaugeChart/rsbuild.config.ts index 51696c9..fb87bc1 100644 --- a/GaugeChart/rsbuild.config.ts +++ b/GaugeChart/rsbuild.config.ts @@ -35,7 +35,7 @@ export default defineConfig({ new ModuleFederationPlugin({ name: 'GaugeChart', exposes: { - './GaugeChart': './src/GaugeChart.tsx', + './GaugeChart': './src/GaugeChart.ts', }, shared: { react: { requiredVersion: '18.2.0', singleton: true }, @@ -51,6 +51,7 @@ export default defineConfig({ '@hookform/resolvers': { singleton: true }, 'use-resize-observer': { requiredVersion: '9.1.0', singleton: true }, 'mdi-material-ui': { requiredVersion: '7.4.0', singleton: true }, + immer: { singleton: true }, }, dts: false, runtime: false, diff --git a/GaugeChart/src/GaugeChart.ts b/GaugeChart/src/GaugeChart.ts new file mode 100644 index 0000000..c68faa8 --- /dev/null +++ b/GaugeChart/src/GaugeChart.ts @@ -0,0 +1,32 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin } from '@perses-dev/plugin-system'; +import { createInitialGaugeChartOptions, GaugeChartOptions } from './gauge-chart-model'; +import { GaugeChartOptionsEditorSettings } from './GaugeChartOptionsEditorSettings'; +import { GaugeChartPanel } from './GaugeChartPanel'; + +/** + * The core GaugeChart panel plugin for Perses. + */ +export const GaugeChart: PanelPlugin = { + PanelComponent: GaugeChartPanel, + supportedQueryTypes: ['TimeSeriesQuery'], + panelOptionsEditorComponents: [ + { + label: 'Settings', + content: GaugeChartOptionsEditorSettings, + }, + ], + createInitialOptions: createInitialGaugeChartOptions, +}; diff --git a/GaugeChart/src/GaugeChartOptionsEditorSettings.test.tsx b/GaugeChart/src/GaugeChartOptionsEditorSettings.test.tsx new file mode 100644 index 0000000..10a1789 --- /dev/null +++ b/GaugeChart/src/GaugeChartOptionsEditorSettings.test.tsx @@ -0,0 +1,102 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ChartsProvider, testChartsTheme } from '@perses-dev/components'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { GaugeChartOptions } from './gauge-chart-model'; +import { GaugeChartOptionsEditorSettings } from './GaugeChartOptionsEditorSettings'; + +describe('GaugeChartOptionsEditorSettings', () => { + const renderGaugeChartOptionsEditorSettings = (value: GaugeChartOptions, onChange = jest.fn()): void => { + render( + + + + ); + }; + + it('can modify unit', () => { + const onChange = jest.fn(); + renderGaugeChartOptionsEditorSettings( + { + format: { + unit: 'decimal', + }, + calculation: 'first', + }, + onChange + ); + const unitSelector = screen.getByRole('combobox', { name: 'Unit' }); + userEvent.click(unitSelector); + const yearOption = screen.getByRole('option', { + name: 'Years', + }); + userEvent.click(yearOption); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + format: { + unit: 'years', + }, + }) + ); + }); + + it('can modify calculation', () => { + const onChange = jest.fn(); + renderGaugeChartOptionsEditorSettings( + { + format: { + unit: 'days', + }, + calculation: 'first', + }, + onChange + ); + const calcSelector = screen.getByRole('combobox', { name: 'Calculation' }); + userEvent.click(calcSelector); + const meanOption = screen.getByRole('option', { + name: /Average/, + }); + userEvent.click(meanOption); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + calculation: 'mean', + }) + ); + }); + + it('can modify max', async () => { + let maxValue: number | undefined = undefined; + const onChange = jest.fn((e) => { + maxValue = e.max; + }); + renderGaugeChartOptionsEditorSettings( + { + format: { + unit: 'decimal', + }, + max: 1, + calculation: 'last-number', + }, + onChange + ); + const maxInput = await screen.findByLabelText(/Max/); + expect(maxInput).toBeInTheDocument(); + userEvent.clear(maxInput); + userEvent.type(maxInput, '5'); + expect(onChange).toHaveBeenCalledTimes(2); + expect(maxValue).toBe(5); + }); +}); diff --git a/GaugeChart/src/GaugeChartOptionsEditorSettings.tsx b/GaugeChart/src/GaugeChartOptionsEditorSettings.tsx new file mode 100644 index 0000000..7c154f3 --- /dev/null +++ b/GaugeChart/src/GaugeChartOptionsEditorSettings.tsx @@ -0,0 +1,107 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { TextField } from '@mui/material'; +import { + FormatControls, + FormatControlsProps, + OptionsEditorColumn, + OptionsEditorControl, + OptionsEditorGrid, + OptionsEditorGroup, + ThresholdsEditor, +} from '@perses-dev/components'; +import { ThresholdOptions } from '@perses-dev/core'; +import { CalculationSelector, CalculationSelectorProps } from '@perses-dev/plugin-system'; +import { produce } from 'immer'; +import merge from 'lodash/merge'; +import { ReactElement } from 'react'; +import { + DEFAULT_FORMAT, + DEFAULT_MAX_PERCENT, + DEFAULT_MAX_PERCENT_DECIMAL, + GaugeChartOptions, + GaugeChartOptionsEditorProps, +} from './gauge-chart-model'; + +export function GaugeChartOptionsEditorSettings(props: GaugeChartOptionsEditorProps): ReactElement { + const { onChange, value } = props; + + const handleCalculationChange: CalculationSelectorProps['onChange'] = (newCalculation) => { + onChange( + produce(value, (draft: GaugeChartOptions) => { + draft.calculation = newCalculation; + }) + ); + }; + + const handleUnitChange: FormatControlsProps['onChange'] = (newFormat) => { + onChange( + produce(value, (draft: GaugeChartOptions) => { + draft.format = newFormat; + }) + ); + }; + + // ensures decimalPlaces defaults to correct value + const format = merge({}, DEFAULT_FORMAT, value.format); + + // max only needs to be set explicitly for units other than percent and percent-decimal + let maxPlaceholder = 'Enter value'; + if (format.unit === 'percent') { + maxPlaceholder = DEFAULT_MAX_PERCENT.toString(); + } else if (format.unit === 'percent-decimal') { + maxPlaceholder = DEFAULT_MAX_PERCENT_DECIMAL.toString(); + } + + const handleThresholdsChange = (thresholds: ThresholdOptions): void => { + onChange( + produce(value, (draft: GaugeChartOptions) => { + draft.thresholds = thresholds; + }) + ); + }; + + return ( + + + + + + { + // ensure empty value resets to undef to allow chart to calculate max + const newValue = e.target.value ? Number(e.target.value) : undefined; + onChange( + produce(value, (draft: GaugeChartOptions) => { + draft.max = newValue; + }) + ); + }} + placeholder={maxPlaceholder} + /> + } + /> + + + + + + + ); +} diff --git a/GaugeChart/src/GaugeChart.tsx b/GaugeChart/src/GaugeChartPanel.tsx similarity index 63% rename from GaugeChart/src/GaugeChart.tsx rename to GaugeChart/src/GaugeChartPanel.tsx index 2169e98..ff56977 100644 --- a/GaugeChart/src/GaugeChart.tsx +++ b/GaugeChart/src/GaugeChartPanel.tsx @@ -1,4 +1,4 @@ -// Copyright 2024 The Perses Authors +// Copyright 2023 The Perses Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -11,50 +11,39 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* eslint-disable @typescript-eslint/ban-ts-comment */ -import { GaugeChart as PersesGaugeChart, useChartsTheme } from '@perses-dev/components'; -import { CalculationsMap, DEFAULT_CALCULATION, PanelDefinition } from '@perses-dev/core'; -import { useDataQueries } from '@perses-dev/plugin-system'; -import { GaugeSeriesOption } from 'echarts'; +import { Box, Skeleton, Stack } from '@mui/material'; +import { GaugeChart, GaugeSeries, useChartsTheme } from '@perses-dev/components'; +import { CalculationsMap, DEFAULT_CALCULATION } from '@perses-dev/core'; +import { PanelProps, useDataQueries } from '@perses-dev/plugin-system'; +import type { GaugeSeriesOption } from 'echarts'; import merge from 'lodash/merge'; -import React, { useMemo } from 'react'; +import { ReactElement, useMemo } from 'react'; import { DEFAULT_FORMAT, DEFAULT_MAX_PERCENT, DEFAULT_MAX_PERCENT_DECIMAL, - EMPTY_GAUGE_SERIES, - GAUGE_MIN_WIDTH, GaugeChartOptions, - GaugeSeries, - PANEL_PADDING_OFFSET, -} from './model'; -import { convertThresholds, defaultThresholdInput } from './tresholds'; - -interface ChartProps { - definition: PanelDefinition; - contentDimensions?: { width: number; height: number }; -} +} from './gauge-chart-model'; +import { convertThresholds, defaultThresholdInput } from './thresholds'; -export function GaugeChart(props: ChartProps) { - const { - definition: { - spec: { - plugin: { - spec: { calculation, max, format: pluginSpecFormat, thresholds: pluginSpecThresholds }, - }, - }, - }, - contentDimensions, - } = props; +const EMPTY_GAUGE_SERIES: GaugeSeries = { label: '', value: null }; +const GAUGE_MIN_WIDTH = 90; +const PANEL_PADDING_OFFSET = 20; + +export type GaugeChartPanelProps = PanelProps; + +export function GaugeChartPanel(props: GaugeChartPanelProps): ReactElement | null { + const { spec: pluginSpec, contentDimensions } = props; + const { calculation, max } = pluginSpec; const { thresholds: thresholdsColors } = useChartsTheme(); const { queryResults, isLoading } = useDataQueries('TimeSeriesQuery'); // ensures all default format properties set if undef - const format = merge({}, DEFAULT_FORMAT, pluginSpecFormat); + const format = merge({}, DEFAULT_FORMAT, pluginSpec.format); - const thresholds = pluginSpecThresholds ?? defaultThresholdInput; + const thresholds = pluginSpec.thresholds ?? defaultThresholdInput; const gaugeData: GaugeSeries[] = useMemo(() => { if (queryResults[0]?.data === undefined) { @@ -82,8 +71,16 @@ export function GaugeChart(props: ChartProps) { if (contentDimensions === undefined) return null; + // TODO: remove Skeleton, add loading state to match mockups if (isLoading) { - return
Loading...
; + return ( + contentDimensions.height ? contentDimensions.height : contentDimensions.width} + height={contentDimensions.height} + /> + ); } // needed for end value of last threshold color segment @@ -108,7 +105,7 @@ export function GaugeChart(props: ChartProps) { // no data message handled inside chart component if (gaugeData.length === 0) { return ( - 1; + return ( -
+ 1 ? 'scroll' : 'auto', + }} + > {gaugeData.map((series, seriesIndex) => { return ( -
- + -
+ ); })} -
+ ); } diff --git a/GaugeChart/src/model.ts b/GaugeChart/src/gauge-chart-model.ts similarity index 50% rename from GaugeChart/src/model.ts rename to GaugeChart/src/gauge-chart-model.ts index 326b20d..e993ee8 100644 --- a/GaugeChart/src/model.ts +++ b/GaugeChart/src/gauge-chart-model.ts @@ -1,4 +1,4 @@ -// Copyright 2024 The Perses Authors +// Copyright 2023 The Perses Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -11,15 +11,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CalculationType, FormatOptions, ThresholdOptions } from '@perses-dev/core'; +import { CalculationType, Definition, ThresholdOptions, FormatOptions } from '@perses-dev/core'; +import { OptionsEditorProps } from '@perses-dev/plugin-system'; -export const EMPTY_GAUGE_SERIES: GaugeSeries = { label: '', value: null }; -export const GAUGE_MIN_WIDTH = 90; -export const PANEL_PADDING_OFFSET = 20; +export const DEFAULT_FORMAT: FormatOptions = { unit: 'percent-decimal' }; export const DEFAULT_MAX_PERCENT = 100; export const DEFAULT_MAX_PERCENT_DECIMAL = 1; -export const DEFAULT_FORMAT: FormatOptions = { unit: 'percent-decimal' }; +/** + * The schema for a GaugeChart panel. + */ +export interface GaugeChartDefinition extends Definition { + kind: 'GaugeChart'; +} + +/** + * The Options object type supported by the GaugeChart panel plugin. + */ export interface GaugeChartOptions { calculation: CalculationType; format?: FormatOptions; @@ -27,9 +35,24 @@ export interface GaugeChartOptions { max?: number; } -export type GaugeChartValue = number | null | undefined; +export type GaugeChartOptionsEditorProps = OptionsEditorProps; -export type GaugeSeries = { - value: GaugeChartValue; - label: string; -}; +/** + * Creates the initial/empty options for a GaugeChart panel. + */ +export function createInitialGaugeChartOptions(): GaugeChartOptions { + return { + calculation: 'last-number', + format: DEFAULT_FORMAT, + thresholds: { + steps: [ + { + value: 0.8, + }, + { + value: 0.9, + }, + ], + }, + }; +} diff --git a/GaugeChart/src/setup-tests.ts b/GaugeChart/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/GaugeChart/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/GaugeChart/src/tresholds.ts b/GaugeChart/src/thresholds.ts similarity index 72% rename from GaugeChart/src/tresholds.ts rename to GaugeChart/src/thresholds.ts index 66f6c46..de5351d 100644 --- a/GaugeChart/src/tresholds.ts +++ b/GaugeChart/src/thresholds.ts @@ -1,3 +1,16 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import { StepOptions, ThresholdColorPalette, ThresholdOptions, FormatOptions } from '@perses-dev/core'; import zip from 'lodash/zip'; diff --git a/GaugeChart/tsconfig.json b/GaugeChart/tsconfig.json index da01ddb..4eb37fe 100644 --- a/GaugeChart/tsconfig.json +++ b/GaugeChart/tsconfig.json @@ -1,3 +1,3 @@ { "extends": "../tsconfig.base.json" -} \ No newline at end of file +} diff --git a/MarkdownChart/jest.config.ts b/MarkdownChart/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/MarkdownChart/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/MarkdownChart/package.json b/MarkdownChart/package.json index aa1f4db..41c65be 100644 --- a/MarkdownChart/package.json +++ b/MarkdownChart/package.json @@ -1,16 +1,18 @@ { "name": "@perses-dev/markdown-chart", "private": true, - "version": "0.3.0", + "version": "0.4.0", "scripts": { "dev": "rsbuild dev", "build": "rsbuild build", - "lint": "eslint src --ext .ts,.tsx" + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest --passWithNoTests", + "type-check": "tsc --noEmit" }, "dependencies": { "@module-federation/enhanced": "^0.1.11", - "dompurify": "^2.4.7", - "marked": "^4.3.0" + "dompurify": "^3.2.3", + "marked": "^15.0.6" }, "peerDependencies": { "@emotion/react": "^11.7.1", diff --git a/MarkdownChart/rsbuild.config.ts b/MarkdownChart/rsbuild.config.ts index f13cc03..b44cfa6 100644 --- a/MarkdownChart/rsbuild.config.ts +++ b/MarkdownChart/rsbuild.config.ts @@ -35,7 +35,7 @@ export default defineConfig({ new ModuleFederationPlugin({ name: 'MarkdownChart', exposes: { - './MarkdownChart': './src/MarkdownChart.tsx', + './MarkdownChart': './src/MarkdownChart.ts', }, shared: { react: { requiredVersion: '18.2.0', singleton: true }, diff --git a/MarkdownChart/src/MarkdownChart.ts b/MarkdownChart/src/MarkdownChart.ts new file mode 100644 index 0000000..352a87e --- /dev/null +++ b/MarkdownChart/src/MarkdownChart.ts @@ -0,0 +1,33 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin } from '@perses-dev/plugin-system'; +import { createInitialMarkdownPanelOptions, MarkdownPanelOptions } from './markdown-panel-model'; +import { MarkdownPanel } from './MarkdownPanel'; +import { MarkdownPanelOptionsEditor } from './MarkdownPanelOptionsEditor'; + +/** + * The core Markdown panel plugin in Perses. + */ +export const MarkdownChart: PanelPlugin = { + PanelComponent: MarkdownPanel, + supportedQueryTypes: [], + panelOptionsEditorComponents: [ + { + label: 'Markdown', + content: MarkdownPanelOptionsEditor, + }, + ], + createInitialOptions: createInitialMarkdownPanelOptions, + hideQueryEditor: true, +}; diff --git a/MarkdownChart/src/MarkdownChart.tsx b/MarkdownChart/src/MarkdownChart.tsx deleted file mode 100644 index 9aff3c0..0000000 --- a/MarkdownChart/src/MarkdownChart.tsx +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2024 The Perses Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* eslint-disable @typescript-eslint/ban-ts-comment */ -import { useChartsTheme } from '@perses-dev/components'; -import { PanelDefinition } from '@perses-dev/core'; -import { useReplaceVariablesInString } from '@perses-dev/plugin-system'; -import DOMPurify from 'dompurify'; -import { marked } from 'marked'; -import React, { useMemo } from 'react'; -import { MarkdownPanelOptions } from './model'; -import './styles.css'; - -interface ChartProps { - definition: PanelDefinition; - contentDimensions?: { width: number; height: number }; -} - -// Convert markdown to HTML -// Supports original markdown and GitHub Flavored markdown -function markdownToHTML(text: string): string { - return marked.parse(text, { gfm: true }); -} - -// Prevent XSS attacks by removing the vectors for attacks -function sanitizeHTML(html: string): string { - return DOMPurify.sanitize(html); -} - -export function MarkdownChart(props: ChartProps) { - const { - definition: { - spec: { - plugin: { - spec: { text }, - }, - }, - }, - } = props; - - const chartsTheme = useChartsTheme(); - - const textAfterVariableReplacement = useReplaceVariablesInString(text); - - const html = useMemo(() => markdownToHTML(textAfterVariableReplacement ?? ''), [textAfterVariableReplacement]); - const sanitizedHTML = useMemo(() => sanitizeHTML(html), [html]); - - return ( -
- ); -} diff --git a/MarkdownChart/src/MarkdownPanel.tsx b/MarkdownChart/src/MarkdownPanel.tsx new file mode 100644 index 0000000..a5df5de --- /dev/null +++ b/MarkdownChart/src/MarkdownPanel.tsx @@ -0,0 +1,95 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Box, Theme } from '@mui/material'; +import { PersesChartsTheme, useChartsTheme } from '@perses-dev/components'; +import { PanelProps, useReplaceVariablesInString } from '@perses-dev/plugin-system'; +import DOMPurify from 'dompurify'; +import { marked } from 'marked'; +import React, { ReactElement, useMemo } from 'react'; +import { MarkdownPanelOptions } from './markdown-panel-model'; + +export type MarkdownPanelProps = PanelProps; + +function createMarkdownPanelStyles(theme: Theme, chartsTheme: PersesChartsTheme): Record { + return { + padding: `${chartsTheme.container.padding.default}px`, + // Make the content scrollable + height: '100%', + overflowY: 'auto', + // Ignore top margin on the first element. + '& :first-of-type': { + marginTop: 0, + }, + // Styles for headers + '& h1': { + fontSize: '2em', + }, + // Styles for + '& code': { fontSize: '0.85em' }, + '& :not(pre) code': { + padding: '0.2em 0.4em', + backgroundColor: theme.palette.grey[100], + borderRadius: '4px', + }, + '& pre': { + padding: '1.2em', + backgroundColor: theme.palette.grey[100], + borderRadius: '4px', + }, + // Styles for + '& table, & th, & td': { + padding: '0.6em', + border: `1px solid ${theme.palette.grey[300]}`, + borderCollapse: 'collapse', + }, + // Styles for
  • + '& li + li': { + marginTop: '0.25em', + }, + // Styles for + '& a': { + color: theme.palette.primary.main, + }, + }; +} + +// Convert markdown to HTML +// Supports original markdown and GitHub Flavored markdown +function markdownToHTML(text: string): string { + return marked.parse(text, { gfm: true, async: false }); +} + +// Prevent XSS attacks by removing the vectors for attacks +function sanitizeHTML(html: string): string { + return DOMPurify.sanitize(html); +} + +export function MarkdownPanel(props: MarkdownPanelProps): ReactElement { + const { + spec: { text }, + } = props; + const chartsTheme = useChartsTheme(); + + const textAfterVariableReplacement = useReplaceVariablesInString(text); + + const html = useMemo(() => markdownToHTML(textAfterVariableReplacement ?? ''), [textAfterVariableReplacement]); + const sanitizedHTML = useMemo(() => sanitizeHTML(html), [html]); + + return ( + createMarkdownPanelStyles(theme, chartsTheme)} + dangerouslySetInnerHTML={{ __html: sanitizedHTML }} + /> + ); +} diff --git a/MarkdownChart/src/MarkdownPanelOptionsEditor.tsx b/MarkdownChart/src/MarkdownPanelOptionsEditor.tsx new file mode 100644 index 0000000..617c29d --- /dev/null +++ b/MarkdownChart/src/MarkdownPanelOptionsEditor.tsx @@ -0,0 +1,50 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ChangeEvent, ReactElement } from 'react'; +import { OptionsEditorProps } from '@perses-dev/plugin-system'; +import { Link, Stack, TextField } from '@mui/material'; +import { MarkdownPanelOptions } from './markdown-panel-model'; + +export type MarkdownPanelOptionsEditorProps = OptionsEditorProps; + +const MARKDOWN_GUIDE_URL = 'https://commonmark.org/help/'; +const TEXT_INPUT_NUM_ROWS = 20; + +export function MarkdownPanelOptionsEditor(props: MarkdownPanelOptionsEditorProps): ReactElement { + const { + onChange, + value: { text }, + } = props; + + const handleChange = (e: ChangeEvent): void => { + onChange({ text: e.target.value }); + }; + + return ( + + + Markdown Guide + + + + ); +} diff --git a/MarkdownChart/src/markdown-panel-model.ts b/MarkdownChart/src/markdown-panel-model.ts new file mode 100644 index 0000000..fa18b78 --- /dev/null +++ b/MarkdownChart/src/markdown-panel-model.ts @@ -0,0 +1,29 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Definition } from '@perses-dev/core'; + +/** + * The schema for a Markdown panel. + */ +export interface MarkdownPanelDefinition extends Definition { + kind: 'MarkdownChart'; +} + +export interface MarkdownPanelOptions { + text: string; +} + +export function createInitialMarkdownPanelOptions(): MarkdownPanelOptions { + return { text: '' }; +} diff --git a/MarkdownChart/src/setup-tests.ts b/MarkdownChart/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/MarkdownChart/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/MarkdownChart/tsconfig.json b/MarkdownChart/tsconfig.json index da01ddb..4eb37fe 100644 --- a/MarkdownChart/tsconfig.json +++ b/MarkdownChart/tsconfig.json @@ -1,3 +1,3 @@ { "extends": "../tsconfig.base.json" -} \ No newline at end of file +} diff --git a/PieChart/README.md b/PieChart/README.md new file mode 100644 index 0000000..a4a8709 --- /dev/null +++ b/PieChart/README.md @@ -0,0 +1,23 @@ +# Perses Panel Plugin + +## Setup + +Install dependencies: + +```bash +npm install +``` + +## Get Started + +Start the dev server: + +```bash +npm run dev +``` + +Build the plugin for distribution: + +```bash +npm run build +``` diff --git a/PieChart/jest.config.ts b/PieChart/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/PieChart/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/PieChart/package.json b/PieChart/package.json new file mode 100644 index 0000000..b744718 --- /dev/null +++ b/PieChart/package.json @@ -0,0 +1,47 @@ +{ + "name": "@perses-dev/pie-chart", + "private": true, + "version": "0.4.0", + "scripts": { + "dev": "rsbuild dev", + "build": "rsbuild build", + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@module-federation/enhanced": "^0.1.11" + }, + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "@perses-dev/components": "^0.49.0", + "@perses-dev/core": "^0.49.0", + "@perses-dev/plugin-system": "^0.49.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0", + "immer": "^9.0.15" + }, + "files": [ + "dist" + ], + "perses": { + "plugins": [ + { + "kind": "Panel", + "spec": { + "display": { + "name": "Pie Chart" + }, + "name": "PieChart" + } + } + ] + } +} diff --git a/PieChart/rsbuild.config.ts b/PieChart/rsbuild.config.ts new file mode 100644 index 0000000..08496ee --- /dev/null +++ b/PieChart/rsbuild.config.ts @@ -0,0 +1,59 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack'; +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; + +export default defineConfig({ + server: { + port: 3005, + }, + dev: { + assetPrefix: '/plugins/PieChart/', + }, + output: { + assetPrefix: '/plugins/PieChart/', + copy: [{ from: './package.json' }, { from: 'README.md' }], + }, + plugins: [pluginReact()], + tools: { + htmlPlugin: false, + rspack: (config, { appendPlugins }) => { + config.output!.uniqueName = 'PieChart'; + appendPlugins([ + new ModuleFederationPlugin({ + name: 'PieChart', + exposes: { + './PieChart': './src/PieChart.ts', + }, + shared: { + react: { requiredVersion: '18.2.0', singleton: true }, + 'react-dom': { requiredVersion: '18.2.0', singleton: true }, + echarts: { singleton: true }, + 'date-fns': { singleton: true }, + 'date-fns-tz': { singleton: true }, + lodash: { singleton: true }, + '@perses-dev/components': { singleton: true }, + '@perses-dev/plugin-system': { singleton: true }, + '@emotion/react': { requiredVersion: '^11.11.3', singleton: true }, + '@emotion/styled': { singleton: true }, + '@hookform/resolvers': { singleton: true }, + }, + dts: false, + runtime: false, + }), + ]); + }, + }, +}); diff --git a/PieChart/src/PieChart.ts b/PieChart/src/PieChart.ts new file mode 100644 index 0000000..dffd8ee --- /dev/null +++ b/PieChart/src/PieChart.ts @@ -0,0 +1,32 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin } from '@perses-dev/plugin-system'; +import { createInitialPieChartOptions, PieChartOptions } from './pie-chart-model'; +import { PieChartOptionsEditorSettings } from './PieChartOptionsEditorSettings'; +import { PieChartPanel } from './PieChartPanel'; + +/** + * The core PieChart panel plugin for Perses. + */ +export const PieChart: PanelPlugin = { + PanelComponent: PieChartPanel, + panelOptionsEditorComponents: [ + { + label: 'Settings', + content: PieChartOptionsEditorSettings, + }, + ], + supportedQueryTypes: ['TimeSeriesQuery'], + createInitialOptions: createInitialPieChartOptions, +}; diff --git a/PieChart/src/PieChartOptionsEditorSettings.tsx b/PieChart/src/PieChartOptionsEditorSettings.tsx new file mode 100644 index 0000000..d74ee10 --- /dev/null +++ b/PieChart/src/PieChartOptionsEditorSettings.tsx @@ -0,0 +1,118 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import merge from 'lodash/merge'; +import { + CalculationSelector, + CalculationSelectorProps, + LegendOptionsEditor, + LegendOptionsEditorProps, +} from '@perses-dev/plugin-system'; +import { produce } from 'immer'; +import { + FormatControls, + FormatControlsProps, + OptionsEditorGroup, + OptionsEditorGrid, + OptionsEditorColumn, + SortSelector, + SortSelectorProps, + ModeSelector, + ModeSelectorProps, + ModeOption, + SortOption, +} from '@perses-dev/components'; +import { CalculationType, isPercentUnit, FormatOptions } from '@perses-dev/core'; +import { Button } from '@mui/material'; +import { ReactElement } from 'react'; +import { PieChartOptions, PieChartOptionsEditorProps, DEFAULT_FORMAT } from './pie-chart-model'; + +export function PieChartOptionsEditorSettings(props: PieChartOptionsEditorProps): ReactElement { + const { onChange, value } = props; + + const handleCalculationChange: CalculationSelectorProps['onChange'] = (newCalculation: CalculationType) => { + onChange( + produce(value, (draft: PieChartOptions) => { + draft.calculation = newCalculation; + }) + ); + }; + + const handleLegendChange: LegendOptionsEditorProps['onChange'] = (newLegend) => { + // TODO (sjcobb): fix type, add position, fix glitch + onChange( + produce(value, (draft: PieChartOptions) => { + draft.legend = newLegend; + }) + ); + }; + + const handleUnitChange: FormatControlsProps['onChange'] = (newFormat: FormatOptions) => { + onChange( + produce(value, (draft: PieChartOptions) => { + draft.format = newFormat; + }) + ); + }; + + const handleSortChange: SortSelectorProps['onChange'] = (newSort: SortOption) => { + onChange( + produce(value, (draft: PieChartOptions) => { + draft.sort = newSort; + }) + ); + }; + + const handleModeChange: ModeSelectorProps['onChange'] = (newMode: ModeOption) => { + onChange( + produce(value, (draft: PieChartOptions) => { + draft.mode = newMode; + }) + ); + }; + + // ensures decimalPlaces defaults to correct value + const format = merge({}, DEFAULT_FORMAT, value.format); + + return ( + + + + + + + + + + + + + + + + + ); +} diff --git a/PieChart/src/PieChartPanel.tsx b/PieChart/src/PieChartPanel.tsx new file mode 100644 index 0000000..4b19f7e --- /dev/null +++ b/PieChart/src/PieChartPanel.tsx @@ -0,0 +1,200 @@ +//Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { + PieChart, + PieChartData, + LoadingOverlay, + useChartsTheme, + ContentWithLegend, + SelectedLegendItemState, + LegendProps, + ChartInstance, + useId, + LegendItem, +} from '@perses-dev/components'; +import { Box, useTheme } from '@mui/material'; +import { useMemo, useState, useRef, ReactElement } from 'react'; +import { CalculationType, CalculationsMap, DEFAULT_LEGEND } from '@perses-dev/core'; +import { validateLegendSpec, useDataQueries, PanelProps } from '@perses-dev/plugin-system'; +import merge from 'lodash/merge'; +import { getSeriesColor } from './palette-gen'; +import { DEFAULT_VISUAL, QuerySettingsOptions } from './model'; +import { PieChartOptions } from './pie-chart-model'; +import { calculatePercentages, sortSeriesData } from './utils'; + +export type PieChartPanelProps = PanelProps; + +export function PieChartPanel(props: PieChartPanelProps): ReactElement | null { + const { + spec: { calculation, sort, mode, querySettings: querySettingsList }, + contentDimensions, + } = props; + const chartsTheme = useChartsTheme(); + const muiTheme = useTheme(); + const PADDING = chartsTheme.container.padding.default; + const { queryResults, isLoading, isFetching } = useDataQueries('TimeSeriesQuery'); // gets data queries from a context provider, see DataQueriesProvider + const chartId = useId('time-series-panel'); + const categoricalPalette = chartsTheme.echartsTheme.color; + + const visual = useMemo(() => { + return merge({}, DEFAULT_VISUAL, props.spec.visual); + }, [props.spec.visual]); + + const { pieChartData, legendItems } = useMemo(() => { + const calculate = CalculationsMap[calculation as CalculationType]; + const pieChartData: PieChartData[] = []; + const legendItems: LegendItem[] = []; + + for (let queryIndex = 0; queryIndex < queryResults.length; queryIndex++) { + const result = queryResults[queryIndex]; + // Skip queries that are still loading or don't have data + if (!result || result.isLoading || result.isFetching || result.data === undefined) continue; + + let seriesIndex = 0; + for (const seriesData of result.data.series) { + // Retrieve querySettings for this query, if exists. + // queries & querySettings indices do not necessarily match, so we have to check the tail value of the $ref attribute + let querySettings: QuerySettingsOptions | undefined; + for (const item of querySettingsList ?? []) { + if (item.queryIndex === queryIndex) { + querySettings = item; + // We don't break the loop here just in case there are multiple querySettings defined for the + // same queryIndex, because in that case we want the last one to take precedence. + } + } + const seriesColor = getSeriesColor({ + categoricalPalette: categoricalPalette as string[], + visual, + muiPrimaryColor: muiTheme.palette.primary.main, + seriesName: seriesData.name, + seriesIndex, + querySettings: querySettings, + queryHasMultipleResults: (queryResults[queryIndex]?.data?.series?.length ?? 0) > 1, + }); + const series = { + value: calculate(seriesData.values) ?? null, + name: seriesData.formattedName ?? '', + itemStyle: { + color: seriesColor, + }, + }; + pieChartData.push(series); + + const seriesId = chartId + seriesData.name + seriesIndex; + legendItems.push({ + id: seriesId, + label: series.name, + color: seriesColor, + }); + seriesIndex++; + } + } + + const sortedPieChartData = sortSeriesData(pieChartData, sort); + if (mode === 'percentage') { + return { + pieChartData: calculatePercentages(sortedPieChartData), + legendItems, + }; + } + return { + pieChartData: sortedPieChartData, + legendItems, + }; + }, [ + calculation, + sort, + mode, + queryResults, + categoricalPalette, + visual, + muiTheme.palette.primary.main, + chartId, + querySettingsList, + ]); + + const contentPadding = chartsTheme.container.padding.default; + const adjustedContentDimensions: typeof contentDimensions = contentDimensions + ? { + width: contentDimensions.width - contentPadding * 2, + height: contentDimensions.height - contentPadding * 2, + } + : undefined; + + const legend = useMemo(() => { + return props.spec.legend && validateLegendSpec(props.spec.legend) + ? merge({}, DEFAULT_LEGEND, props.spec.legend) + : undefined; + }, [props.spec.legend]); + + const [selectedLegendItems, setSelectedLegendItems] = useState('ALL'); + + const [legendSorting, setLegendSorting] = useState['sorting']>(); + + const chartRef = useRef(null); + + // ensures there are fallbacks for unset properties since most + // users should not need to customize visual display + + if (queryResults[0]?.error) throw queryResults[0]?.error; + if (contentDimensions === undefined) return null; + + if (isLoading || isFetching) { + return ; + } + + return ( + + { + chartRef.current?.highlightSeries({ name: id }); + }, + onItemMouseOut: (): void => { + chartRef.current?.clearHighlightedSeries(); + }, + } + } + > + {({ height, width }) => { + return ( + + + + ); + }} + + + ); +} diff --git a/PieChart/src/bootstrap.tsx b/PieChart/src/bootstrap.tsx new file mode 100644 index 0000000..3380d4a --- /dev/null +++ b/PieChart/src/bootstrap.tsx @@ -0,0 +1,18 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +const root = ReactDOM.createRoot(document.getElementById('root')!); +root.render(); diff --git a/PieChart/src/env.d.ts b/PieChart/src/env.d.ts new file mode 100644 index 0000000..b302dd9 --- /dev/null +++ b/PieChart/src/env.d.ts @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// diff --git a/PieChart/src/index.tsx b/PieChart/src/index.tsx new file mode 100644 index 0000000..d3d1d92 --- /dev/null +++ b/PieChart/src/index.tsx @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import('./bootstrap'); diff --git a/PieChart/src/model.ts b/PieChart/src/model.ts new file mode 100644 index 0000000..fa831d9 --- /dev/null +++ b/PieChart/src/model.ts @@ -0,0 +1,19 @@ +export const DEFAULT_CONNECT_NULLS = false; +export const DEFAULT_LINE_WIDTH = 1.25; +export const POINT_SIZE_OFFSET = 1.5; +export const DEFAULT_POINT_RADIUS = DEFAULT_LINE_WIDTH + POINT_SIZE_OFFSET; +export const DEFAULT_AREA_OPACITY = 0; +export type StackOptions = 'none' | 'all'; + +export interface QuerySettingsOptions { + queryIndex: number; + colorMode: 'fixed' | 'fixed-single'; + colorValue: string; +} + +export const DEFAULT_VISUAL = { + lineWidth: DEFAULT_LINE_WIDTH, + areaOpacity: DEFAULT_AREA_OPACITY, + pointRadius: DEFAULT_POINT_RADIUS, + connectNulls: DEFAULT_CONNECT_NULLS, +}; diff --git a/PieChart/src/options-model.ts b/PieChart/src/options-model.ts new file mode 100644 index 0000000..f1bb1e6 --- /dev/null +++ b/PieChart/src/options-model.ts @@ -0,0 +1,35 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export interface QuerySettingsOptions { + queryIndex: number; + colorMode: 'fixed' | 'fixed-single'; + colorValue: string; +} + +export interface ChartPaletteOptions { + mode: 'auto' | 'categorical'; +} + +export type StackOptions = 'none' | 'all'; + +export type ChartVisualOptions = { + display?: 'line' | 'bar'; + lineWidth?: number; + areaOpacity?: number; + showPoints?: 'auto' | 'always'; + palette?: ChartPaletteOptions; + pointRadius?: number; + stack?: StackOptions; + connectNulls?: boolean; +}; diff --git a/PieChart/src/palette-gen.ts b/PieChart/src/palette-gen.ts new file mode 100644 index 0000000..ad391b9 --- /dev/null +++ b/PieChart/src/palette-gen.ts @@ -0,0 +1,94 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { getConsistentColor } from './palette'; +import { QuerySettingsOptions, ChartVisualOptions } from './options-model'; + +export interface SeriesColorProps { + categoricalPalette: string[]; + visual: ChartVisualOptions; + muiPrimaryColor: string; + seriesName: string; + seriesIndex: number; + querySettings?: QuerySettingsOptions; + queryHasMultipleResults?: boolean; +} + +/** + * Get line color as well as color for tooltip and legend, account for whether palette is 'categorical' or 'auto' aka generative + */ +export function getSeriesColor(props: SeriesColorProps): string { + const { + categoricalPalette, + visual, + muiPrimaryColor, + seriesName, + seriesIndex, + querySettings, + queryHasMultipleResults, + } = props; + + // Use color overrides defined in query settings in priority, if applicable + if (querySettings) { + if (querySettings.colorMode === 'fixed') { + return querySettings.colorValue; + } else if (querySettings.colorMode === 'fixed-single' && !queryHasMultipleResults) { + return querySettings.colorValue; + } + } + + // Fallback is unlikely to set unless echarts theme palette in charts theme provider is undefined. + const fallbackColor = + Array.isArray(categoricalPalette) && categoricalPalette[0] + ? (categoricalPalette[0] as string) // Needed since echarts color property isn't always an array. + : muiPrimaryColor; + + // Explicit way to always cycle through classical palette instead of changing when based on number of series. + if (visual.palette?.mode === 'categorical') { + return getCategoricalPaletteColor(categoricalPalette, seriesIndex, fallbackColor); + } + + return getAutoPaletteColor(seriesName, fallbackColor); +} + +/** + * Get color from generative color palette, this approaches uses series name as the seed and + * allows for consistent colors across panels (when all panels use this approach). + */ +export function getAutoPaletteColor(name: string, fallbackColor: string): string { + // corresponds to 'Auto' in palette.kind for generative color palette + const generatedColor = getConsistentSeriesNameColor(name); + return generatedColor ?? fallbackColor; +} + +/** + * Default classical qualitative palette that cycles through the colors array by index. + */ +export function getCategoricalPaletteColor(palette: string[], seriesIndex: number, fallbackColor: string): string { + if (palette === undefined) { + return fallbackColor; + } + // Loop through predefined static color palette + const paletteTotalColors = palette.length ?? 1; + const paletteIndex = seriesIndex % paletteTotalColors; + // fallback color comes from echarts theme + const seriesColor = palette[paletteIndex] ?? fallbackColor; + return seriesColor; +} + +/* + * Generate a consistent series name color (if series name includes 'error', it will have a red hue). + */ +export function getConsistentSeriesNameColor(inputString: string): string { + return getConsistentColor(inputString, inputString.toLowerCase().includes('error')); +} diff --git a/PieChart/src/palette.ts b/PieChart/src/palette.ts new file mode 100644 index 0000000..a0fa7ed --- /dev/null +++ b/PieChart/src/palette.ts @@ -0,0 +1,66 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import ColorHash from 'color-hash'; + +// Valid hue values are 0 to 360 and can be adjusted to control the generated colors. +// More info: https://github.com/zenozeng/color-hash#custom-hue +// Picked min of 20 and max of 360 to exclude common threshold colors (red). +// Items with "error" in them will always be generated as red. +const ERROR_HUE_CUTOFF = 20; +const colorGenerator = new ColorHash({ hue: { min: ERROR_HUE_CUTOFF, max: 360 } }); +const redColorGenerator = new ColorHash({ hue: { min: 0, max: ERROR_HUE_CUTOFF } }); + +function computeConsistentColor(name: string, error: boolean): string { + const [hue, saturation, lightness] = error ? redColorGenerator.hsl(name) : colorGenerator.hsl(name); + const saturationPercent = `${(saturation * 100).toFixed(0)}%`; + const lightnessPercent = `${(lightness * 100).toFixed(0)}%`; + return `hsla(${hue.toFixed(2)},${saturationPercent},${lightnessPercent},0.9)`; +} + +// To check whether a color has already been generated for a given string. +// TODO: Predefined color aliases will be defined here +const colorLookup: Record = {}; + +/** + * Return a consistent color for (name, error) tuple + */ +export function getConsistentColor(name: string, error: boolean): string { + const key = `${name}_____${error}`; + let value = colorLookup[key]; + if (!value) { + value = computeConsistentColor(name, error); + colorLookup[key] = value; + } + return value; +} + +export function getConsistentCategoricalColor( + name: string, + error: boolean, + categoricalPalette: string[], + errorPalette: string[] +): string { + const palette = error ? errorPalette : categoricalPalette; + if (palette.length === 0) { + console.warn('getConsistentCategoricalColor() called with empty color palette, fallback to #000'); + return '#000'; + } + + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = name.charCodeAt(i) + ((hash << 5) - hash); + } + + return palette[Math.abs(hash) % palette.length] ?? '#000'; +} diff --git a/PieChart/src/pie-chart-model.ts b/PieChart/src/pie-chart-model.ts new file mode 100644 index 0000000..63872c6 --- /dev/null +++ b/PieChart/src/pie-chart-model.ts @@ -0,0 +1,107 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ModeOption, SortOption } from '@perses-dev/components'; +import { CalculationType, DEFAULT_CALCULATION, Definition, FormatOptions } from '@perses-dev/core'; +import { OptionsEditorProps, LegendSpecOptions } from '@perses-dev/plugin-system'; + +import { DEFAULT_CONNECT_NULLS, DEFAULT_POINT_RADIUS, StackOptions } from './model'; + +export const DEFAULT_FORMAT: FormatOptions = { unit: 'decimal', shortValues: true }; +export const DEFAULT_SORT: SortOption = 'desc'; +export const DEFAULT_MODE: ModeOption = 'value'; +export const DEFAULT_LINE_WIDTH = 1.25; +export const DEFAULT_AREA_OPACITY = 0; + +export const VISUAL_CONFIG = { + lineWidth: { + label: 'Line Width', + testId: 'slider-line-width', + min: 0.25, + max: 3, + step: 0.25, + }, + pointRadius: { + label: 'Point Radius', + testId: 'slider-point-radius', + min: 0, + max: 6, + step: 0.25, + }, + areaOpacity: { + label: 'Area Opacity', + testId: 'slider-area-opacity', + min: 0, + max: 1, + step: 0.05, + }, + stack: { + label: 'Stack Series', + }, + connectNulls: { + label: 'Connect Nulls', + }, +}; + +export interface BarChartDefinition extends Definition { + kind: 'PieChart'; +} +export const DEFAULT_VISUAL: PieChartVisualOptions = { + lineWidth: DEFAULT_LINE_WIDTH, + areaOpacity: DEFAULT_AREA_OPACITY, + pointRadius: DEFAULT_POINT_RADIUS, + connectNulls: DEFAULT_CONNECT_NULLS, +}; +export interface PieChartPaletteOptions { + mode: 'auto' | 'categorical'; +} + +export interface PieChartOptions { + legend?: LegendSpecOptions; + visual?: PieChartVisualOptions; + querySettings?: QuerySettingsOptions[]; + calculation: CalculationType; + radius: number; + format?: FormatOptions; + sort?: SortOption; + mode?: ModeOption; +} + +export interface QuerySettingsOptions { + queryIndex: number; + colorMode: 'fixed' | 'fixed-single'; + colorValue: string; +} +export type PieChartVisualOptions = { + display?: 'line' | 'bar'; + lineWidth?: number; + areaOpacity?: number; + showPoints?: 'auto' | 'always'; + palette?: PieChartPaletteOptions; + pointRadius?: number; + stack?: StackOptions; + connectNulls?: boolean; +}; + +export type PieChartOptionsEditorProps = OptionsEditorProps; + +export function createInitialPieChartOptions(): PieChartOptions { + return { + calculation: DEFAULT_CALCULATION, + format: DEFAULT_FORMAT, + radius: 50, + sort: DEFAULT_SORT, + mode: DEFAULT_MODE, + visual: DEFAULT_VISUAL, + }; +} diff --git a/PieChart/src/setup-tests.ts b/PieChart/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/PieChart/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/PieChart/src/utils.test.ts b/PieChart/src/utils.test.ts new file mode 100644 index 0000000..c4dc5bb --- /dev/null +++ b/PieChart/src/utils.test.ts @@ -0,0 +1,120 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PieChartData } from '@perses-dev/components'; +import { calculatePercentages, sortSeriesData } from './utils'; + +const MOCK_DATA: PieChartData[] = [ + { + name: 'Label 1', + value: 3, + }, + { + name: 'Label 2', + value: 2, + }, + { + name: 'Label 3', + value: 0, + }, + { + name: 'Label 4', + value: 5, + }, + { + name: 'Label 5', + value: null, + }, +]; + +describe('calculatePercentages', () => { + it('calculates correct percentage values', () => { + const percentages = calculatePercentages(MOCK_DATA); + expect(percentages).toEqual([ + { + name: 'Label 1', + value: 30, + }, + { + name: 'Label 2', + value: 20, + }, + { + name: 'Label 3', + value: 0, + }, + { + name: 'Label 4', + value: 50, + }, + { + name: 'Label 5', + value: 0, + }, + ]); + }); +}); + +describe('sortSeriesData', () => { + it('sorts in ascending order', () => { + const sorted = sortSeriesData(MOCK_DATA, 'asc'); + expect(sorted).toEqual([ + { + name: 'Label 4', + value: 5, + }, + { + name: 'Label 1', + value: 3, + }, + { + name: 'Label 2', + value: 2, + }, + { + name: 'Label 3', + value: 0, + }, + { + name: 'Label 5', + value: null, + }, + ]); + }); + + it('sorts in descending order', () => { + const sorted = sortSeriesData(MOCK_DATA, 'desc'); + expect(sorted).toEqual([ + { + name: 'Label 5', + value: null, + }, + { + name: 'Label 3', + value: 0, + }, + { + name: 'Label 2', + value: 2, + }, + { + name: 'Label 1', + value: 3, + }, + { + name: 'Label 4', + value: 5, + }, + ]); + }); +}); diff --git a/PieChart/src/utils.ts b/PieChart/src/utils.ts new file mode 100644 index 0000000..94daa50 --- /dev/null +++ b/PieChart/src/utils.ts @@ -0,0 +1,58 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PieChartData, SortOption } from '@perses-dev/components'; +import { DEFAULT_SORT } from './pie-chart-model'; + +export function calculatePercentages(data: PieChartData[]): Array<{ name: string; value: number }> { + const sum = data.reduce((accumulator, { value }) => accumulator + (value ?? 0), 0); + return data.map((seriesData) => { + const percentage = ((seriesData.value ?? 0) / sum) * 100; + return { + ...seriesData, + value: percentage, + }; + }); +} + +export function sortSeriesData(data: PieChartData[], sortOrder: SortOption = DEFAULT_SORT): PieChartData[] { + if (sortOrder === 'asc') { + // sort in ascending order by value + return data.sort((a, b) => { + if (a.value === null) { + return 1; + } + if (b.value === null) { + return -1; + } + if (a.value === b.value) { + return 0; + } + return a.value < b.value ? 1 : -1; + }); + } else { + // sort in descending order by value + return data.sort((a, b) => { + if (a.value === null) { + return -1; + } + if (b.value === null) { + return 1; + } + if (a.value === b.value) { + return 0; + } + return a.value < b.value ? -1 : 1; + }); + } +} diff --git a/PieChart/tsconfig.json b/PieChart/tsconfig.json new file mode 100644 index 0000000..4eb37fe --- /dev/null +++ b/PieChart/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.base.json" +} diff --git a/Prometheus/jest.config.ts b/Prometheus/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/Prometheus/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/Prometheus/package.json b/Prometheus/package.json index 6a01664..a39801a 100644 --- a/Prometheus/package.json +++ b/Prometheus/package.json @@ -1,11 +1,13 @@ { "name": "@perses-dev/prometheus", "private": true, - "version": "0.2.0", + "version": "0.4.0", "scripts": { "dev": "rsbuild dev", "build": "rsbuild build", - "lint": "eslint src --ext .ts,.tsx" + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" }, "dependencies": { "@module-federation/enhanced": "^0.1.11", @@ -25,7 +27,10 @@ "lodash": "^4.17.21", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0", - "use-resize-observer": "^9.0.0" + "use-resize-observer": "^9.0.0", + "react-hook-form": "^7.52.2", + "@tanstack/react-query": "^4.36.1", + "immer": "^9.0.15" }, "files": [ "dist" diff --git a/Prometheus/rsbuild.config.ts b/Prometheus/rsbuild.config.ts index 0ce7ddd..2c3a3ca 100644 --- a/Prometheus/rsbuild.config.ts +++ b/Prometheus/rsbuild.config.ts @@ -53,6 +53,8 @@ export default defineConfig({ '@emotion/react': { requiredVersion: '^11.11.3', singleton: true }, '@emotion/styled': { singleton: true }, '@hookform/resolvers': { singleton: true }, + '@tanstack/react-query': { singleton: true }, + 'react-hook-form': { singleton: true }, }, dts: false, runtime: false, diff --git a/Prometheus/src/plugins/prometheus-variables.tsx b/Prometheus/src/plugins/prometheus-variables.tsx index 8a93a2e..23bf61b 100644 --- a/Prometheus/src/plugins/prometheus-variables.tsx +++ b/Prometheus/src/plugins/prometheus-variables.tsx @@ -10,6 +10,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +import { FormControl, InputLabel, Stack, TextField } from '@mui/material'; import { DatasourceSelect, DatasourceSelectProps, @@ -17,9 +18,9 @@ import { useDatasourceClient, VariableOption, } from '@perses-dev/plugin-system'; -import { FormControl, InputLabel, Stack, TextField } from '@mui/material'; import { produce } from 'immer'; import { ReactElement } from 'react'; +import { PromQLEditor } from '../components'; import { DEFAULT_PROM, isDefaultPromSelector, @@ -29,13 +30,12 @@ import { PrometheusClient, VectorData, } from '../model'; -import { PromQLEditor } from '../components'; +import { MatcherEditor } from './MatcherEditor'; import { PrometheusLabelNamesVariableOptions, PrometheusLabelValuesVariableOptions, PrometheusPromQLVariableOptions, } from './types'; -import { MatcherEditor } from './MatcherEditor'; export function PrometheusLabelValuesVariableEditor( props: OptionsEditorProps diff --git a/Prometheus/src/setup-tests.ts b/Prometheus/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/Prometheus/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/ScatterChart/README.md b/ScatterChart/README.md new file mode 100644 index 0000000..a4a8709 --- /dev/null +++ b/ScatterChart/README.md @@ -0,0 +1,23 @@ +# Perses Panel Plugin + +## Setup + +Install dependencies: + +```bash +npm install +``` + +## Get Started + +Start the dev server: + +```bash +npm run dev +``` + +Build the plugin for distribution: + +```bash +npm run build +``` diff --git a/ScatterChart/jest.config.ts b/ScatterChart/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/ScatterChart/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/ScatterChart/package.json b/ScatterChart/package.json new file mode 100644 index 0000000..db5dc14 --- /dev/null +++ b/ScatterChart/package.json @@ -0,0 +1,49 @@ +{ + "name": "@perses-dev/scatter-chart", + "private": true, + "version": "0.4.0", + "scripts": { + "dev": "rsbuild dev", + "build": "rsbuild build", + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@module-federation/enhanced": "^0.1.11" + }, + "devDependencies": { + "react-virtuoso": "^4.12.2" + }, + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "@perses-dev/components": "^0.49.0", + "@perses-dev/core": "^0.49.0", + "@perses-dev/plugin-system": "^0.49.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0" + }, + "files": [ + "dist" + ], + "perses": { + "plugins": [ + { + "kind": "Panel", + "spec": { + "display": { + "name": "Scatter Chart" + }, + "name": "ScatterChart" + } + } + ] + } +} diff --git a/ScatterChart/rsbuild.config.ts b/ScatterChart/rsbuild.config.ts new file mode 100644 index 0000000..71b104c --- /dev/null +++ b/ScatterChart/rsbuild.config.ts @@ -0,0 +1,59 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack'; +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; + +export default defineConfig({ + server: { + port: 3005, + }, + dev: { + assetPrefix: '/plugins/ScatterChart/', + }, + output: { + assetPrefix: '/plugins/ScatterChart/', + copy: [{ from: './package.json' }, { from: 'README.md' }], + }, + plugins: [pluginReact()], + tools: { + htmlPlugin: false, + rspack: (config, { appendPlugins }) => { + config.output!.uniqueName = 'ScatterChart'; + appendPlugins([ + new ModuleFederationPlugin({ + name: 'ScatterChart', + exposes: { + './ScatterChart': './src/ScatterChart.ts', + }, + shared: { + react: { requiredVersion: '18.2.0', singleton: true }, + 'react-dom': { requiredVersion: '18.2.0', singleton: true }, + echarts: { singleton: true }, + 'date-fns': { singleton: true }, + 'date-fns-tz': { singleton: true }, + lodash: { singleton: true }, + '@perses-dev/components': { singleton: true }, + '@perses-dev/plugin-system': { singleton: true }, + '@emotion/react': { requiredVersion: '^11.11.3', singleton: true }, + '@emotion/styled': { singleton: true }, + '@hookform/resolvers': { singleton: true }, + }, + dts: false, + runtime: false, + }), + ]); + }, + }, +}); diff --git a/ScatterChart/src/ScatterChart.ts b/ScatterChart/src/ScatterChart.ts new file mode 100644 index 0000000..fba8c9a --- /dev/null +++ b/ScatterChart/src/ScatterChart.ts @@ -0,0 +1,27 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin } from '@perses-dev/plugin-system'; +import { createInitialScatterChartOptions, ScatterChartOptions } from './scatter-chart-model'; +import { ScatterChartPanel, ScatterChartPanelProps } from './ScatterChartPanel'; + +/** + * The core ScatterChart panel plugin for Perses. + */ +export const ScatterChart: PanelPlugin = { + PanelComponent: ScatterChartPanel, + // TODO: add a chart options editor plugin, for example: + // panelOptionsEditorComponents: [{ label: 'Settings', content: ScatterChartOptionsEditorSettings }], + supportedQueryTypes: ['TraceQuery'], + createInitialOptions: createInitialScatterChartOptions, +}; diff --git a/ScatterChart/src/ScatterChartPanel.test.tsx b/ScatterChart/src/ScatterChartPanel.test.tsx new file mode 100644 index 0000000..4f4284c --- /dev/null +++ b/ScatterChart/src/ScatterChartPanel.test.tsx @@ -0,0 +1,117 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { + TraceQueryPlugin, + MockPlugin, + useDataQueries, + PluginRegistry, + TimeRangeContext, + mockPluginRegistry, +} from '@perses-dev/plugin-system'; +import { UnknownSpec, TimeRangeValue, toAbsoluteTimeRange } from '@perses-dev/core'; +import { screen, render } from '@testing-library/react'; +import { VirtuosoMockContext } from 'react-virtuoso'; +import { ChartsProvider, testChartsTheme } from '@perses-dev/components'; +import { + MOCK_TRACE_SEARCH_RESULT, + MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT, + MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT_EMPTY, +} from './mock-trace-data'; +import { getSymbolSize, ScatterChartPanel, ScatterChartPanelProps } from './ScatterChartPanel'; + +jest.mock('@perses-dev/plugin-system', () => { + return { + ...jest.requireActual('@perses-dev/plugin-system'), + useDataQueries: jest.fn(), + }; +}); + +const FakeTraceQueryPlugin: TraceQueryPlugin = { + getTraceData: async () => { + return MOCK_TRACE_SEARCH_RESULT; + }, + createInitialOptions: () => ({}), +}; + +const MOCK_TRACE_QUERY_PLUGIN: MockPlugin = { + kind: 'TempoTraceQuery', + pluginType: 'TraceQuery', + plugin: FakeTraceQueryPlugin, +}; + +const TEST_SCATTER_PANEL: ScatterChartPanelProps = { + contentDimensions: { + width: 500, + height: 500, + }, + spec: {}, +}; + +const TEST_TIME_RANGE: TimeRangeValue = { pastDuration: '1h' }; + +describe('ScatterChartPanel', (): void => { + const renderPanel = (): void => { + const mockTimeRangeContext = { + refreshIntervalInMs: 0, + setRefreshInterval: () => ({}), + timeRange: TEST_TIME_RANGE, + setTimeRange: () => ({}), + absoluteTimeRange: toAbsoluteTimeRange(TEST_TIME_RANGE), + refresh: jest.fn(), + refreshKey: `${TEST_TIME_RANGE.pastDuration}:0`, + }; + render( + + + + + + + + + + ); + }; + + it('should render a ScatterPlot', async () => { + (useDataQueries as jest.Mock).mockReturnValue({ + queryResults: MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT, + isLoading: false, + isFetching: false, + }); + renderPanel(); + expect(await screen.findByTestId('ScatterChartPanel_ScatterPlot')).toBeInTheDocument(); + }); + + it('should not render a ScatterPlot because trace results are empty', async () => { + (useDataQueries as jest.Mock).mockReturnValue({ + queryResults: MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT_EMPTY, + isLoading: false, + isFetching: false, + }); + renderPanel(); + // expect it to return a Alert because the query produces no trace results + expect(await screen.findByText('No traces')).toBeInTheDocument(); + }); + + it('should scale the circles', () => { + // apply linear scale from range [1,5] to a value from range [10,20] + expect(getSymbolSize(1, [1, 5], [10, 20])).toEqual(10); + expect(getSymbolSize(3, [1, 5], [10, 20])).toEqual(15); + expect(getSymbolSize(5, [1, 5], [10, 20])).toEqual(20); + + // use max size if all span counts are same + expect(getSymbolSize(5, [5, 5], [10, 20])).toEqual(20); + }); +}); diff --git a/ScatterChart/src/ScatterChartPanel.tsx b/ScatterChart/src/ScatterChartPanel.tsx new file mode 100644 index 0000000..e630f2a --- /dev/null +++ b/ScatterChart/src/ScatterChartPanel.tsx @@ -0,0 +1,212 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelProps, useDataQueries } from '@perses-dev/plugin-system'; +import { ReactElement, useMemo } from 'react'; +import { QueryDefinition, TraceSearchResult } from '@perses-dev/core'; +import { EChartsOption, SeriesOption } from 'echarts'; +import { LoadingOverlay, NoDataOverlay, useChartsTheme } from '@perses-dev/components'; +import { Scatterplot } from './Scatterplot'; +import { ScatterChartOptions } from './scatter-chart-model'; + +export interface EChartTraceValue extends Omit { + name: string; + query: QueryDefinition; + startTime: Date; + spanCount: number; + errorCount: number; +} + +export interface ScatterChartPanelProps extends PanelProps { + /** + * Custom onClick event handler. + * If this field is unset or undefined, a default handler which links to the Gantt chart on the explore page is configured. + * Set this field explicitly to null to disable handling the click event. + */ + onClick?: ((data: EChartTraceValue) => void) | null; +} + +/** default size range of the circles diameter */ +const DEFAULT_SIZE_RANGE: [number, number] = [6, 20]; + +// Navigate to the Gantt Chart on the explore page by default +function defaultClickHandler(data: EChartTraceValue): void { + // clone the original query spec (including the datasource) and replace the query value with the trace id + const query: QueryDefinition = JSON.parse(JSON.stringify(data.query)); + query.spec.plugin.spec.query = data.traceId; + + const exploreParams = new URLSearchParams({ + explorer: 'traces', + data: JSON.stringify({ queries: [query] }), + }); + + // do not use react-router here, as downstream products, which embed this panel, may not have a compatible version of it + window.location.href = `/explore?${exploreParams}`; +} + +/** + * ScatterChartPanel receives data from the DataQueriesProvider and transforms it + * into a `dataset` object that Apache ECharts can consume. Additionally, + * data formatting is also dictated in this component. Formatting includes + * datapoint size and color. + * + * Documentation for data structures accepted by Apache ECharts: + * https://echarts.apache.org/handbook/en/concepts/dataset + * + * Examples for scatter chart formatting in Apache ECharts: + * https://echarts.apache.org/examples/en/index.html#chart-type-scatter + * + * @returns a `ScatterPlot` component that contains an EChart which will handle + * visualization of the data. + */ +export function ScatterChartPanel(props: ScatterChartPanelProps): ReactElement | null { + const { spec, contentDimensions, onClick } = props; + const { queryResults: traceResults, isLoading: traceIsLoading } = useDataQueries('TraceQuery'); + const chartsTheme = useChartsTheme(); + const defaultColor = chartsTheme.thresholds.defaultColor || 'blue'; + const sizeRange = spec.sizeRange || DEFAULT_SIZE_RANGE; + + // Generate dataset + // Transform Tempo API response to fit 'dataset' structure from Apache ECharts + // https://echarts.apache.org/handbook/en/concepts/dataset + const { dataset, minSpanCount, maxSpanCount } = useMemo(() => { + if (traceIsLoading) { + return { dataset: [], minSpanCount: 0, maxSpanCount: 0 }; + } + + const dataset = []; + let minSpanCount: number | undefined; + let maxSpanCount: number | undefined; + for (const result of traceResults) { + if (result.isLoading || result.data === undefined || result.data.searchResult === undefined) continue; + const dataSeries = result.data.searchResult.map((trace) => { + let spanCount = 0; + let errorCount = 0; + for (const stats of Object.values(trace.serviceStats)) { + spanCount += stats.spanCount; + errorCount += stats.errorCount ?? 0; + } + + if (minSpanCount === undefined || spanCount < minSpanCount) { + minSpanCount = spanCount; + } + if (maxSpanCount === undefined || spanCount > maxSpanCount) { + maxSpanCount = spanCount; + } + + const newTraceValue: EChartTraceValue = { + ...trace, + query: result.definition, + name: `${trace.rootServiceName}: ${trace.rootTraceName}`, + startTime: new Date(trace.startTimeUnixMs), // convert unix epoch time to Date + spanCount, + errorCount, + }; + return newTraceValue; + }); + dataset.push({ + source: dataSeries, + }); + } + return { dataset, minSpanCount: minSpanCount ?? 0, maxSpanCount: maxSpanCount ?? 0 }; + }, [traceIsLoading, traceResults]); + + // Formatting for the dataset + // 1. Map x,y coordinates + // 2. Datapoint size corresponds to the number of spans in a trace + // 3. Color datapoint red if the trace contains an error + const series = useMemo(() => { + const seriesTemplate2: SeriesOption = { + type: 'scatter', + encode: { + // Map to x-axis. + x: 'startTime', + // Map to y-axis. + y: 'durationMs', + }, + symbolSize: function (data) { + // returns the diameter of the circles + return getSymbolSize(data.spanCount, [minSpanCount, maxSpanCount], sizeRange); + }, + itemStyle: { + color: function (params) { + const traceData: EChartTraceValue = params.data as EChartTraceValue; + // If the trace contains an error, color the datapoint in red + if (traceData.errorCount > 0) { + return 'red'; + } + // Else return default color + return defaultColor; + }, + }, + }; + + // Each data set needs to have a corresponding series formatting object + const series = []; + for (let i = 0; i < dataset.length; i++) { + series.push({ ...seriesTemplate2, datasetIndex: i }); + } + return series; + }, [dataset, defaultColor, minSpanCount, maxSpanCount, sizeRange]); + + if (traceIsLoading) { + return ; + } + + const queryError = traceResults.find((d) => d.error); + if (queryError) { + throw queryError.error; + } + + const tracesFound = traceResults.some((traceData) => (traceData.data?.searchResult ?? []).length > 0); + if (!tracesFound) { + return ; + } + + const options: EChartsOption = { + dataset: dataset, + series: series, + }; + + if (contentDimensions === undefined) return null; + + return ( +
    + +
    + ); +} + +// exported for tests +export function getSymbolSize( + spanCount: number, + spanCountRange: [number, number], + sizeRange: [number, number] +): number { + const [minSize, maxSize] = sizeRange; + const [minSpanCount, maxSpanCount] = spanCountRange; + + // catch divison by zero + if (maxSpanCount - minSpanCount === 0) { + return maxSize; + } + + // apply linear scale of spanCount from range [minSpanCount,maxSpanCount] to a value from range [minSize,maxSize] + const rel = (spanCount - minSpanCount) / (maxSpanCount - minSpanCount); + return minSize + (maxSize - minSize) * rel; +} diff --git a/ScatterChart/src/Scatterplot.tsx b/ScatterChart/src/Scatterplot.tsx new file mode 100644 index 0000000..d567d6a --- /dev/null +++ b/ScatterChart/src/Scatterplot.tsx @@ -0,0 +1,129 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ReactElement, useMemo } from 'react'; +import { EChart, OnEventsType, useChartsTheme } from '@perses-dev/components'; +import { use, EChartsCoreOption } from 'echarts/core'; +import { ScatterChart as EChartsScatterChart } from 'echarts/charts'; +import { + DatasetComponent, + DataZoomComponent, + LegendComponent, + GridComponent, + TitleComponent, + TooltipComponent, +} from 'echarts/components'; +import { CanvasRenderer } from 'echarts/renderers'; +import { EChartsOption, ScatterSeriesOption } from 'echarts'; +import { formatValue } from '@perses-dev/core'; +import { EChartTraceValue } from './ScatterChartPanel'; + +use([ + DatasetComponent, + DataZoomComponent, + LegendComponent, + EChartsScatterChart, + GridComponent, + TitleComponent, + TooltipComponent, + CanvasRenderer, +]); + +interface ScatterplotProps { + width: number; + height: number; + options: EChartsOption; + onClick?: (data: T) => void; +} + +const DATE_FORMATTER = new Intl.DateTimeFormat(undefined, { + dateStyle: 'long', + timeStyle: 'medium', +}).format; + +export function Scatterplot(props: ScatterplotProps): ReactElement { + const { width, height, options, onClick } = props; + const chartsTheme = useChartsTheme(); + + // Apache EChart Options Docs: https://echarts.apache.org/en/option.html + const eChartOptions: EChartsCoreOption = { + dataset: options.dataset, + series: options.series, + dataZoom: options.dataZoom, + grid: { + bottom: 40, + top: 50, + left: 50, + right: 100, + }, + xAxis: { + type: 'time', + name: 'Local Time', + }, + yAxis: { + scale: true, + type: 'value', + name: 'Duration', + axisLabel: { + formatter: (durationMs: number) => formatValue(durationMs, { unit: 'milliseconds' }), + }, + }, + animation: false, + tooltip: { + padding: 5, + borderWidth: 1, + trigger: 'axis', + axisPointer: { + type: 'cross', + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + formatter: function (params: any) { + // TODO: import type from ECharts instead of using any + const data = params[0].data as EChartTraceValue; + return [ + `Service name: ${data.rootServiceName}
    `, + `Span name: ${data.rootTraceName}
    `, + `Time: ${DATE_FORMATTER(data.startTime)}
    `, + `Duration: ${formatValue(data.durationMs, { unit: 'milliseconds' })}
    `, + `Span count: ${data.spanCount} (${data.errorCount} errors)
    `, + ].join(''); + }, + }, + legend: { + show: true, + type: 'scroll', + orient: 'horizontal', + bottom: 0, + }, + }; + + const handleEvents: OnEventsType = useMemo(() => { + const handlers: OnEventsType = {}; + if (onClick) { + handlers.click = (params): void => onClick(params.data as T); + } + return handlers; + }, [onClick]); + + return ( + + ); +} diff --git a/ScatterChart/src/bootstrap.tsx b/ScatterChart/src/bootstrap.tsx new file mode 100644 index 0000000..3380d4a --- /dev/null +++ b/ScatterChart/src/bootstrap.tsx @@ -0,0 +1,18 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +const root = ReactDOM.createRoot(document.getElementById('root')!); +root.render(); diff --git a/ScatterChart/src/env.d.ts b/ScatterChart/src/env.d.ts new file mode 100644 index 0000000..b302dd9 --- /dev/null +++ b/ScatterChart/src/env.d.ts @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// diff --git a/ScatterChart/src/get-trace-data-jaeger.ts b/ScatterChart/src/get-trace-data-jaeger.ts new file mode 100644 index 0000000..5933190 --- /dev/null +++ b/ScatterChart/src/get-trace-data-jaeger.ts @@ -0,0 +1,157 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Span, SpanEvent, Trace, TraceAttribute, TraceAttributeValue, TraceResource } from '@perses-dev/core'; + +// the following jaeger data types and parsing function should eventually be moved to a jaeger plugin + +export interface JaegerTrace { + traceID: string; + spans: JaegerSpan[]; + processes: unknown; + warnings: unknown; +} + +export interface JaegerSpan { + traceID: string; + spanID: string; + hasChildren: boolean; + childSpanIds: string[]; + depth: number; + processID: string; + process: JaegerProcess; + + operationName: string; + /** start time in microseconds */ + startTime: number; + relativeStartTime: number; + duration: number; + tags: JaegerTag[]; + references: unknown; + logs: unknown; + warnings: unknown; +} + +interface JaegerProcess { + serviceName: string; + tags: JaegerTag[]; +} + +type JaegerTag = + | { + type: 'string'; + key: string; + value: string; + } + | { + type: 'int64'; + key: string; + value: number; + }; + +function parseTag(tags: JaegerTag): TraceAttribute { + let value: TraceAttributeValue; + switch (tags.type) { + case 'string': + value = { stringValue: tags.value }; + break; + case 'int64': + value = { intValue: tags.value.toString() }; + break; + default: + // eslint-disable-next-line @typescript-eslint/no-explicit-any + throw new Error(`unknown jaeger tag type ${(tags as any).type}`); + } + return { key: tags.key, value }; +} + +function parseProcess(process: JaegerProcess): TraceResource { + return { + serviceName: process.serviceName, + attributes: process.tags.map(parseTag), + }; +} + +function parseSpan(span: JaegerSpan): { + traceId: string; + spanId: string; + name: string; + kind: string; + startTimeUnixMs: number; + endTimeUnixMs: number; + attributes: TraceAttribute[]; + events: SpanEvent[]; + status: Record; +} { + return { + traceId: span.traceID, + spanId: span.spanID, + name: span.operationName, + kind: '', + startTimeUnixMs: span.startTime / 1000, + endTimeUnixMs: (span.startTime + span.duration) / 1000, + attributes: span.tags.map(parseTag), + events: [], + status: {}, + }; +} + +export function parseJaegerTrace(jaegerTrace: JaegerTrace): Trace { + // first pass: build lookup table + const lookup = new Map(); + for (const jaegerSpan of jaegerTrace.spans) { + const span: Span = { + resource: parseProcess(jaegerSpan.process), + scope: { + name: jaegerSpan.processID, + }, + childSpans: [], + ...parseSpan(jaegerSpan), + }; + lookup.set(jaegerSpan.spanID, span); + } + + // second pass: build tree based on childSpanIds property + let rootSpan: Span | null = null; + for (const jaegerSpan of jaegerTrace.spans) { + const span = lookup.get(jaegerSpan.spanID); + if (!span) { + continue; + } + + if (jaegerSpan.depth === 0) { + rootSpan = span; + } + + const childSpans: Span[] = []; + for (const childSpanId of jaegerSpan.childSpanIds) { + const childSpan = lookup.get(childSpanId); + if (!childSpan) { + continue; + } + + childSpan.parentSpan = span; + childSpan.parentSpanId = span.spanId; + childSpans.push(childSpan); + } + span.childSpans = childSpans.sort((a, b) => a.startTimeUnixMs - b.startTimeUnixMs); + } + + if (!rootSpan) { + throw new Error('root span not found'); + } + + return { + rootSpan, + }; +} diff --git a/ScatterChart/src/index.tsx b/ScatterChart/src/index.tsx new file mode 100644 index 0000000..d3d1d92 --- /dev/null +++ b/ScatterChart/src/index.tsx @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import('./bootstrap'); diff --git a/ScatterChart/src/mock-trace-data.ts b/ScatterChart/src/mock-trace-data.ts new file mode 100644 index 0000000..7510822 --- /dev/null +++ b/ScatterChart/src/mock-trace-data.ts @@ -0,0 +1,891 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Span, Trace, TraceData } from '@perses-dev/core'; +import { JaegerTrace, parseJaegerTrace } from './get-trace-data-jaeger'; + +export interface GanttTrace { + rootSpan: Span; + + // computed properties of the rootSpan + startTimeUnixMs: number; + endTimeUnixMs: number; +} +function addParentReferences(span: Span): void { + for (const child of span.childSpans) { + child.parentSpan = span; + addParentReferences(child); + } +} + +/** + * Mock data we get from getTraceData() in @perses/tempo-plugin. + */ +export const MOCK_TRACE_SEARCH_RESULT: TraceData = { + searchResult: [ + { + startTimeUnixMs: 1702915645000, // unix epoch time in milliseconds + durationMs: 100, + serviceStats: { + 'service-name': { + spanCount: 10, + }, + 'second-service-name': { + spanCount: 3, + errorCount: 2, + }, + }, + traceId: '123', + rootServiceName: 'service-name', + rootTraceName: 'span-name', + }, + ], + metadata: { + executedQueryString: '{duration > 500ms}', + }, +}; + +export const MOCK_TRACE_SEARCH_RESULT_EMPTY: TraceData = { + searchResult: [], + metadata: { + executedQueryString: '{duration > 500ms}', + }, +}; + +/** + * Mocks results obtained from useTraceQueries() in @perses/plugin-system/runtime. + * This function uses then React TanStack function useQueries(fooQuery) to + * handle fetching. + */ +export const MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: MOCK_TRACE_SEARCH_RESULT, + dataUpdatedAt: 1666500979895, + definition: { + kind: 'TraceQuery', + spec: { + plugin: { + kind: 'TempoTraceQuery', + spec: { + query: '{}', + datasource: { + kind: 'TempoDatasource', + name: 'tempolocal', + }, + }, + }, + }, + }, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT_EMPTY = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: MOCK_TRACE_SEARCH_RESULT_EMPTY, + dataUpdatedAt: 1666500979895, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TRACE: Trace = { + rootSpan: { + resource: { + serviceName: 'shop-backend', + attributes: [{ key: 'service.name', value: { stringValue: 'shop-backend' } }], + }, + scope: { name: 'k6' }, + traceId: 'tid1', + spanId: 'sid1', + name: 'testRootSpan', + kind: 'SPAN_KIND_SERVER', + startTimeUnixMs: 1000, + endTimeUnixMs: 2000, + attributes: [], + events: [], + childSpans: [ + { + resource: { + serviceName: 'shop-backend', + attributes: [{ key: 'service.name', value: { stringValue: 'shop-backend' } }], + }, + scope: { name: 'k6' }, + childSpans: [ + { + resource: { + serviceName: 'shop-backend', + attributes: [{ key: 'service.name', value: { stringValue: 'shop-backend' } }], + }, + scope: { name: 'k6' }, + childSpans: [], + traceId: 'tid1', + spanId: 'sid3', + parentSpanId: 'sid2', + name: 'testChildSpan3', + kind: 'SPAN_KIND_CLIENT', + startTimeUnixMs: 1300, + endTimeUnixMs: 1450, + attributes: [{ key: 'http.method', value: { stringValue: 'PUT' } }], + events: [], + }, + ], + traceId: 'tid1', + spanId: 'sid2', + parentSpanId: 'sid1', + name: 'testChildSpan2', + kind: 'SPAN_KIND_CLIENT', + startTimeUnixMs: 1100, + endTimeUnixMs: 1200, + attributes: [{ key: 'http.method', value: { stringValue: 'DELETE' } }], + events: [ + { + timeUnixMs: 1150, + name: 'event1_name', + attributes: [{ key: 'event1_key', value: { stringValue: 'event1_value' } }], + }, + ], + status: { message: 'Forbidden', code: 'STATUS_CODE_ERROR' }, + }, + ], + }, +}; +addParentReferences(MOCK_TRACE.rootSpan); + +export const MOCK_GANTT_TRACE: GanttTrace = { + rootSpan: MOCK_TRACE.rootSpan, + startTimeUnixMs: MOCK_TRACE.rootSpan.startTimeUnixMs, + endTimeUnixMs: MOCK_TRACE.rootSpan.endTimeUnixMs, +}; + +const MOCK_JAEGER_TRACE_ASYNC: JaegerTrace = { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spans: [ + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '5b6c5367c8f55a07', + operationName: 'post1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599633651, + duration: 3081, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'POST', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path/id-24', + }, + ], + logs: [ + { + timestamp: 1729001599633766, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599636650, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599636673, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 49, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '0bd40fdf749d38af', + operationName: 'get1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599733700, + duration: 2719, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'GET', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path?id=id-24', + }, + ], + logs: [ + { + timestamp: 1729001599733877, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599736343, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599736364, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 100098, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '8a83db29894c10c4', + operationName: 'put1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599833654, + duration: 2803, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'PUT', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path/id-24', + }, + ], + logs: [ + { + timestamp: 1729001599833794, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599836396, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599836408, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 200052, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '4653459b582b47cb', + operationName: 'delete1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599944592, + duration: 20156, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'DELETE', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path/id-24', + }, + ], + logs: [ + { + timestamp: 1729001599944786, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599964619, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599964650, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 310990, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + ], + processes: { + p1: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + }, + warnings: null, +}; + +export const MOCK_TRACE_ASYNC = parseJaegerTrace(MOCK_JAEGER_TRACE_ASYNC); diff --git a/ScatterChart/src/scatter-chart-model.ts b/ScatterChart/src/scatter-chart-model.ts new file mode 100644 index 0000000..1245395 --- /dev/null +++ b/ScatterChart/src/scatter-chart-model.ts @@ -0,0 +1,35 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { UnknownSpec, QueryDefinition } from '@perses-dev/core'; + +export type TraceQueryDefinition = QueryDefinition<'TraceQuery', PluginSpec>; + +/** + * The Options object type supported by the ScatterChart panel plugin. + */ +// TODO: Add support for scatter chart formatting options. +// Some scaffolding has been done to support formatting options. +// This includes the interface below which still needs implementation. +// Note: The interface attributes must match cue/schemas/panels/scatter/scatter.cue +export interface ScatterChartOptions { + /** range of the circles diameter */ + sizeRange?: [number, number]; +} + +/** + * Creates the initial/empty options for a ScatterChart panel. + */ +export function createInitialScatterChartOptions(): Record { + return {}; +} diff --git a/ScatterChart/src/setup-tests.ts b/ScatterChart/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/ScatterChart/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/ScatterChart/tsconfig.json b/ScatterChart/tsconfig.json new file mode 100644 index 0000000..4eb37fe --- /dev/null +++ b/ScatterChart/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.base.json" +} diff --git a/StatChart/jest.config.ts b/StatChart/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/StatChart/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/StatChart/package.json b/StatChart/package.json index e04b0c6..eefd06a 100644 --- a/StatChart/package.json +++ b/StatChart/package.json @@ -1,11 +1,13 @@ { "name": "@perses-dev/stat-chart", "private": true, - "version": "0.3.0", + "version": "0.4.0", "scripts": { "dev": "rsbuild dev", "build": "rsbuild build", - "lint": "eslint src --ext .ts,.tsx" + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" }, "dependencies": { "@module-federation/enhanced": "^0.1.11" @@ -23,7 +25,8 @@ "lodash": "^4.17.21", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0", - "use-resize-observer": "^9.0.0" + "use-resize-observer": "^9.0.0", + "immer": "^9.0.15" }, "files": [ "dist" diff --git a/StatChart/rsbuild.config.ts b/StatChart/rsbuild.config.ts index 676986a..cbfd8f1 100644 --- a/StatChart/rsbuild.config.ts +++ b/StatChart/rsbuild.config.ts @@ -35,7 +35,7 @@ export default defineConfig({ new ModuleFederationPlugin({ name: 'StatChart', exposes: { - './StatChart': './src/StatChart.tsx', + './StatChart': './src/StatChart.ts', }, shared: { react: { requiredVersion: '18.2.0', singleton: true }, diff --git a/StatChart/src/StatChart.ts b/StatChart/src/StatChart.ts new file mode 100644 index 0000000..2e18158 --- /dev/null +++ b/StatChart/src/StatChart.ts @@ -0,0 +1,32 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin } from '@perses-dev/plugin-system'; +import { createInitialStatChartOptions, StatChartOptions } from './stat-chart-model'; +import { StatChartOptionsEditorSettings } from './StatChartOptionsEditorSettings'; +import { StatChartPanel } from './StatChartPanel'; + +/** + * The core StatChart panel plugin for Perses. + */ +export const StatChart: PanelPlugin = { + PanelComponent: StatChartPanel, + supportedQueryTypes: ['TimeSeriesQuery'], + panelOptionsEditorComponents: [ + { + label: 'Settings', + content: StatChartOptionsEditorSettings, + }, + ], + createInitialOptions: createInitialStatChartOptions, +}; diff --git a/StatChart/src/StatChartOptionsEditorSettings.test.tsx b/StatChart/src/StatChartOptionsEditorSettings.test.tsx new file mode 100644 index 0000000..d556fdd --- /dev/null +++ b/StatChart/src/StatChartOptionsEditorSettings.test.tsx @@ -0,0 +1,125 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ChartsProvider, testChartsTheme } from '@perses-dev/components'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { StatChartOptions } from './stat-chart-model'; +import { StatChartOptionsEditorSettings } from './StatChartOptionsEditorSettings'; + +describe('StatChartOptionsEditorSettings', () => { + const renderStatChartOptionsEditorSettings = (value: StatChartOptions, onChange = jest.fn()): void => { + render( + + + + ); + }; + + it('can modify unit', () => { + const onChange = jest.fn(); + renderStatChartOptionsEditorSettings( + { + format: { + unit: 'minutes', + }, + calculation: 'last', + }, + onChange + ); + const unitSelector = screen.getByRole('combobox', { name: 'Unit' }); + userEvent.click(unitSelector); + const percentOption = screen.getByRole('option', { + name: 'Percent (0-100)', + }); + userEvent.click(percentOption); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + format: { + unit: 'percent', + }, + }) + ); + }); + + it('can modify calculation', () => { + const onChange = jest.fn(); + renderStatChartOptionsEditorSettings( + { + format: { + unit: 'days', + }, + calculation: 'sum', + }, + onChange + ); + const calcSelector = screen.getByRole('combobox', { name: 'Calculation' }); + userEvent.click(calcSelector); + const firstOption = screen.getByRole('option', { + name: /First \*/, + }); + userEvent.click(firstOption); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + calculation: 'first-number', + }) + ); + }); + + it('can enable a sparkline', () => { + const onChange = jest.fn(); + renderStatChartOptionsEditorSettings( + { + format: { + unit: 'days', + }, + calculation: 'sum', + }, + onChange + ); + const sparklineSwitch = screen.getByRole('checkbox', { name: 'Sparkline' }); + expect(sparklineSwitch).not.toBeChecked(); + userEvent.click(sparklineSwitch); + + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + sparkline: {}, + }) + ); + }); + + it('can disable a sparkline', () => { + const onChange = jest.fn(); + renderStatChartOptionsEditorSettings( + { + format: { + unit: 'days', + }, + calculation: 'sum', + sparkline: { + color: '#ff0000', + }, + }, + onChange + ); + const sparklineSwitch = screen.getByRole('checkbox', { name: 'Sparkline' }); + expect(sparklineSwitch).toBeChecked(); + userEvent.click(sparklineSwitch); + + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + sparkline: undefined, + }) + ); + }); +}); diff --git a/StatChart/src/StatChartOptionsEditorSettings.tsx b/StatChart/src/StatChartOptionsEditorSettings.tsx new file mode 100644 index 0000000..76b373c --- /dev/null +++ b/StatChart/src/StatChartOptionsEditorSettings.tsx @@ -0,0 +1,102 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { produce } from 'immer'; +import { Switch, SwitchProps } from '@mui/material'; +import { CalculationSelector, CalculationSelectorProps } from '@perses-dev/plugin-system'; +import { + FormatControls, + FormatControlsProps, + OptionsEditorGroup, + OptionsEditorGrid, + OptionsEditorColumn, + OptionsEditorControl, + ThresholdsEditorProps, + ThresholdsEditor, + FontSizeSelector, + FontSizeSelectorProps, + FontSizeOption, +} from '@perses-dev/components'; +import merge from 'lodash/merge'; +import { ReactElement } from 'react'; +import { DEFAULT_FORMAT } from './model'; +import { StatChartOptions, StatChartOptionsEditorProps } from './stat-chart-model'; + +export function StatChartOptionsEditorSettings(props: StatChartOptionsEditorProps): ReactElement { + const { onChange, value } = props; + + // ensures decimalPlaces defaults to correct value + const format = merge({}, DEFAULT_FORMAT, value.format); + + const handleCalculationChange: CalculationSelectorProps['onChange'] = (newCalculation) => { + onChange( + produce(value, (draft: StatChartOptions) => { + draft.calculation = newCalculation; + }) + ); + }; + + const handleUnitChange: FormatControlsProps['onChange'] = (newFormat) => { + onChange( + produce(value, (draft: StatChartOptions) => { + draft.format = newFormat; + }) + ); + }; + + const handleSparklineChange: SwitchProps['onChange'] = (_: unknown, checked: boolean) => { + onChange( + produce(value, (draft: StatChartOptions) => { + // For now, setting to an empty object when checked, so the stat chart + // uses the default chart color and line styles. In the future, this + // will likely be configurable in the UI. + draft.sparkline = checked ? {} : undefined; + }) + ); + }; + + const handleThresholdsChange: ThresholdsEditorProps['onChange'] = (thresholds) => { + onChange( + produce(value, (draft: StatChartOptions) => { + draft.thresholds = thresholds; + }) + ); + }; + + const handleFontSizeChange: FontSizeSelectorProps['onChange'] = (fontSize: FontSizeOption) => { + onChange( + produce(value, (draft: StatChartOptions) => { + draft.valueFontSize = fontSize; + }) + ); + }; + + return ( + + + + } + /> + + + + + + + + + + ); +} diff --git a/StatChart/src/StatChart.tsx b/StatChart/src/StatChartPanel.tsx similarity index 68% rename from StatChart/src/StatChart.tsx rename to StatChart/src/StatChartPanel.tsx index e9cd251..15a943a 100644 --- a/StatChart/src/StatChart.tsx +++ b/StatChart/src/StatChartPanel.tsx @@ -1,4 +1,4 @@ -// Copyright 2024 The Perses Authors +// Copyright 2023 The Perses Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -11,37 +11,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* eslint-disable @typescript-eslint/ban-ts-comment */ -import { GraphSeries, StatChart as PersesStatChart, StatChartData, useChartsTheme } from '@perses-dev/components'; -import { - CalculationsMap, - CalculationType, - DEFAULT_CALCULATION, - PanelDefinition, - TimeSeriesData, -} from '@perses-dev/core'; -import { useDataQueries, UseDataQueryResults } from '@perses-dev/plugin-system'; -import React, { useMemo } from 'react'; -import { convertSparkline, getColorFromThresholds } from './data-transform'; -import { StatChartOptions } from './model'; - -interface ChartProps { - definition: PanelDefinition; - contentDimensions?: { width: number; height: number }; -} +import { TitleComponentOption } from 'echarts'; +import { StatChart, StatChartData, useChartsTheme, GraphSeries, LoadingOverlay } from '@perses-dev/components'; +import { Stack, Typography, SxProps } from '@mui/material'; +import { ReactElement, useMemo } from 'react'; +import { CalculationsMap, CalculationType, DEFAULT_CALCULATION, TimeSeriesData } from '@perses-dev/core'; +import { useDataQueries, UseDataQueryResults, PanelProps } from '@perses-dev/plugin-system'; +import { StatChartOptions } from './stat-chart-model'; +import { convertSparkline, getColorFromThresholds } from './utils/data-transform'; const MIN_WIDTH = 100; const SPACING = 2; -export function StatChart(props: ChartProps) { +export type StatChartPanelProps = PanelProps; + +export function StatChartPanel(props: StatChartPanelProps): ReactElement | null { const { - definition: { - spec: { - plugin: { - spec: { calculation, format, sparkline, thresholds, valueFontSize: valueFontSize }, - }, - }, - }, + spec: { calculation, format, sparkline, thresholds, valueFontSize: valueFontSize }, contentDimensions, } = props; @@ -56,7 +42,7 @@ export function StatChart(props: ChartProps) { if (contentDimensions === undefined) return null; if (isLoading || isFetching) { - return
    Loading...
    ; + return ; } // Calculates chart width @@ -66,20 +52,23 @@ export function StatChart(props: ChartProps) { chartWidth = MIN_WIDTH; } + const noDataTextStyle = (chartsTheme.noDataOption.title as TitleComponentOption).textStyle; + return ( -
    {statChartData.length ? ( statChartData.map((series, index) => ( - )) ) : ( -

    No data

    + No data )} -
    + ); } diff --git a/StatChart/src/model.ts b/StatChart/src/model.ts index d21899e..04b90b0 100644 --- a/StatChart/src/model.ts +++ b/StatChart/src/model.ts @@ -13,3 +13,5 @@ export interface StatChartOptions { sparkline?: StatChartSparklineOptions; valueFontSize?: FontSizeOption; } + +export const DEFAULT_FORMAT: FormatOptions = { unit: 'percent-decimal' }; diff --git a/StatChart/src/setup-tests.ts b/StatChart/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/StatChart/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/StatChart/src/stat-chart-model.ts b/StatChart/src/stat-chart-model.ts new file mode 100644 index 0000000..b4a78a1 --- /dev/null +++ b/StatChart/src/stat-chart-model.ts @@ -0,0 +1,48 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CalculationType, Definition, ThresholdOptions, FormatOptions } from '@perses-dev/core'; +import { FontSizeOption } from '@perses-dev/components'; +import { OptionsEditorProps } from '@perses-dev/plugin-system'; + +/** + * The schema for a StatChart panel. + */ +export interface StatChartDefinition extends Definition { + kind: 'StatChart'; +} + +export interface StatChartOptions { + calculation: CalculationType; + format: FormatOptions; + thresholds?: ThresholdOptions; + sparkline?: StatChartSparklineOptions; + valueFontSize?: FontSizeOption; +} + +export interface StatChartSparklineOptions { + color?: string; + width?: number; +} + +export type StatChartOptionsEditorProps = OptionsEditorProps; + +export function createInitialStatChartOptions(): StatChartOptions { + return { + calculation: 'last-number', + format: { + unit: 'decimal', + }, + sparkline: {}, + }; +} diff --git a/StatChart/src/utils/data-transform.test.ts b/StatChart/src/utils/data-transform.test.ts new file mode 100644 index 0000000..623f298 --- /dev/null +++ b/StatChart/src/utils/data-transform.test.ts @@ -0,0 +1,86 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { testChartsTheme } from '@perses-dev/components'; +import { ThresholdOptions } from '@perses-dev/core'; +import { LineSeriesOption } from 'echarts'; +import { StatChartSparklineOptions } from '../stat-chart-model'; +import { convertSparkline, getColorFromThresholds } from './data-transform'; + +const thresholds: ThresholdOptions = { + steps: [ + { + color: 'yellow', + value: 10, + }, + { + color: 'orange', + value: 20, + }, + { + color: 'red', + value: 30, + }, + ], +}; + +describe('getColorFromThresholds', () => { + describe('if threshold is not met', () => { + it('should return thresholds.defaultColor if defined', () => { + const defaultColor = 'purple'; + const value = getColorFromThresholds(testChartsTheme, { ...thresholds, defaultColor }, 5); + expect(value).toEqual(defaultColor); + }); + + it('should return charts theme default threshold color if thresholds.defaultColor is undefined', () => { + const value = getColorFromThresholds(testChartsTheme, thresholds, 5); + expect(value).toEqual(testChartsTheme.thresholds.defaultColor); + }); + }); + + it('should return orange if value meets the threshold', () => { + const value = getColorFromThresholds(testChartsTheme, thresholds, 25); + expect(value).toEqual('orange'); + }); +}); + +describe('convertSparkline', () => { + const sparkline: StatChartSparklineOptions = { + color: 'purple', + }; + + it('should render charts theme default threshold color', () => { + testChartsTheme.thresholds.defaultColor = 'green'; + const options = convertSparkline(testChartsTheme, {}, thresholds, 5) as LineSeriesOption; + expect(options.lineStyle?.color).toEqual('green'); + expect(options.areaStyle?.color).toEqual('green'); + }); + + it('should render threshold default color if threshold is not met ', () => { + const defaultColor = 'purple'; + const options = convertSparkline( + testChartsTheme, + sparkline, + { ...thresholds, defaultColor }, + 5 + ) as LineSeriesOption; + expect(options.lineStyle?.color).toEqual(defaultColor); + expect(options.areaStyle?.color).toEqual(defaultColor); + }); + + it('should render orange if value meets the threshold', () => { + const options = convertSparkline(testChartsTheme, sparkline, thresholds, 25) as LineSeriesOption; + expect(options.lineStyle?.color).toEqual('orange'); + expect(options.areaStyle?.color).toEqual('orange'); + }); +}); diff --git a/StatChart/src/data-transform.ts b/StatChart/src/utils/data-transform.ts similarity index 96% rename from StatChart/src/data-transform.ts rename to StatChart/src/utils/data-transform.ts index 656d1ab..318b41c 100644 --- a/StatChart/src/data-transform.ts +++ b/StatChart/src/utils/data-transform.ts @@ -14,13 +14,13 @@ import { PersesChartsTheme } from '@perses-dev/components'; import { ThresholdOptions } from '@perses-dev/core'; import { LineSeriesOption } from 'echarts/charts'; -import { StatChartSparklineOptions } from './model'; +import { StatChartSparklineOptions } from '../stat-chart-model'; export function getColorFromThresholds( chartsTheme: PersesChartsTheme, thresholds?: ThresholdOptions, value?: number | null -) { +): string { // thresholds color takes priority over other colors const defaultColor = thresholds?.defaultColor ?? chartsTheme.thresholds.defaultColor; diff --git a/StaticListVariable/package.json b/StaticListVariable/package.json index 2731ed5..4861692 100644 --- a/StaticListVariable/package.json +++ b/StaticListVariable/package.json @@ -5,7 +5,9 @@ "scripts": { "dev": "rsbuild dev", "build": "rsbuild build", - "lint": "eslint src --ext .ts,.tsx" + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest --passWithNoTests", + "type-check": "tsc --noEmit" }, "dependencies": { "@module-federation/enhanced": "^0.1.11", diff --git a/StatusHistoryChart/README.md b/StatusHistoryChart/README.md new file mode 100644 index 0000000..a4a8709 --- /dev/null +++ b/StatusHistoryChart/README.md @@ -0,0 +1,23 @@ +# Perses Panel Plugin + +## Setup + +Install dependencies: + +```bash +npm install +``` + +## Get Started + +Start the dev server: + +```bash +npm run dev +``` + +Build the plugin for distribution: + +```bash +npm run build +``` diff --git a/StatusHistoryChart/cue.mod/module.cue b/StatusHistoryChart/cue.mod/module.cue new file mode 100644 index 0000000..9738bc0 --- /dev/null +++ b/StatusHistoryChart/cue.mod/module.cue @@ -0,0 +1,4 @@ +module: "cue.example" +language: { + version: "v0.11.0" +} diff --git a/StatusHistoryChart/jest.config.ts b/StatusHistoryChart/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/StatusHistoryChart/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/StatusHistoryChart/package.json b/StatusHistoryChart/package.json new file mode 100644 index 0000000..5fbdba7 --- /dev/null +++ b/StatusHistoryChart/package.json @@ -0,0 +1,47 @@ +{ + "name": "@perses-dev/status-history-chart", + "private": true, + "version": "0.4.0", + "scripts": { + "dev": "rsbuild dev", + "build": "rsbuild build", + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@module-federation/enhanced": "^0.1.11", + "@perses-dev/components": "^0.50.0-rc.1", + "@perses-dev/core": "^0.50.0-rc.1", + "@perses-dev/plugin-system": "^0.50.0-rc.1" + }, + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0", + "immer": "^9.0.15" + }, + "files": [ + "dist" + ], + "perses": { + "plugins": [ + { + "kind": "Panel", + "spec": { + "display": { + "name": "Status History Chart" + }, + "name": "StatusHistoryChart" + } + } + ] + } +} diff --git a/StatusHistoryChart/rsbuild.config.ts b/StatusHistoryChart/rsbuild.config.ts new file mode 100644 index 0000000..7e8699d --- /dev/null +++ b/StatusHistoryChart/rsbuild.config.ts @@ -0,0 +1,61 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack'; +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; + +export default defineConfig({ + server: { + port: 3005, + }, + dev: { + assetPrefix: '/plugins/StatusHistoryChart/', + }, + output: { + assetPrefix: '/plugins/StatusHistoryChart/', + copy: [{ from: './package.json' }, { from: 'README.md' }], + }, + plugins: [pluginReact()], + tools: { + htmlPlugin: false, + rspack: (config, { appendPlugins }) => { + config.output!.uniqueName = 'StatusHistoryChart'; + appendPlugins([ + new ModuleFederationPlugin({ + name: 'StatusHistoryChart', + exposes: { + './StatusHistoryChart': './src/StatusHistoryChart.ts', + }, + shared: { + react: { requiredVersion: '18.2.0', singleton: true }, + 'react-dom': { requiredVersion: '18.2.0', singleton: true }, + echarts: { singleton: true }, + 'date-fns': { singleton: true }, + 'date-fns-tz': { singleton: true }, + lodash: { singleton: true }, + '@perses-dev/components': { singleton: true }, + '@perses-dev/plugin-system': { singleton: true }, + '@emotion/react': { requiredVersion: '^11.11.3', singleton: true }, + '@emotion/styled': { singleton: true }, + '@hookform/resolvers': { singleton: true }, + '@tanstack/react-query': { singleton: true }, + 'react-hook-form': { singleton: true }, + }, + dts: false, + runtime: false, + }), + ]); + }, + }, +}); diff --git a/StatusHistoryChart/schemas/migrate/migrate.cue b/StatusHistoryChart/schemas/migrate/migrate.cue new file mode 100644 index 0000000..56b80de --- /dev/null +++ b/StatusHistoryChart/schemas/migrate/migrate.cue @@ -0,0 +1,36 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package migrate + +#grafanaType: "status-history" +#panel: _ + +kind: "StatusHistoryChart" +spec: { + #showLegend: *#panel.options.legend.showLegend | true + if #panel.options.legend != _|_ if #showLegend { + legend: { + if #panel.type == "status-history" { + position: [ + if #panel.options.legend.placement != _|_ if #panel.options.legend.placement == "right" {"right"}, + {"bottom"}, + ][0] + mode: [ + if #panel.options.legend.displayMode == "list" {"list"}, + if #panel.options.legend.displayMode == "table" {"table"}, + ][0] + } + } + } +} diff --git a/StatusHistoryChart/schemas/status-history.cue b/StatusHistoryChart/schemas/status-history.cue new file mode 100644 index 0000000..80b783e --- /dev/null +++ b/StatusHistoryChart/schemas/status-history.cue @@ -0,0 +1,31 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "github.com/perses/perses/cue/schemas/common" +) + +#legendValue: common.#calculation + +#legend: { + position: "bottom" | "right" + mode?: "list" | "table" + size?: "small" | "medium" +} + +kind: "StatusHistoryChart" +spec: close({ + legend?: #legend +}) diff --git a/StatusHistoryChart/schemas/status-history.json b/StatusHistoryChart/schemas/status-history.json new file mode 100644 index 0000000..9813848 --- /dev/null +++ b/StatusHistoryChart/schemas/status-history.json @@ -0,0 +1,8 @@ +{ + "kind": "StatusHistoryChart", + "spec": { + "legend": { + "position": "bottom" + } + } +} \ No newline at end of file diff --git a/StatusHistoryChart/src/StatusHistoryChart.ts b/StatusHistoryChart/src/StatusHistoryChart.ts new file mode 100644 index 0000000..627f56d --- /dev/null +++ b/StatusHistoryChart/src/StatusHistoryChart.ts @@ -0,0 +1,25 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin } from '@perses-dev/plugin-system'; + +import { createInitialStatusHistoryChartOptions, StatusHistoryChartOptions } from './status-history-model'; +import { StatusHistoryChartOptionsEditorSettings } from './StatusHistoryChartOptionsEditorSettings'; +import { StatusHistoryPanel } from './StatusHistoryPanel'; + +export const StatusHistoryChart: PanelPlugin = { + PanelComponent: StatusHistoryPanel, + supportedQueryTypes: ['TimeSeriesQuery'], + panelOptionsEditorComponents: [{ label: 'Settings', content: StatusHistoryChartOptionsEditorSettings }], + createInitialOptions: createInitialStatusHistoryChartOptions, +}; diff --git a/StatusHistoryChart/src/StatusHistoryChartOptionsEditorSettings.tsx b/StatusHistoryChart/src/StatusHistoryChartOptionsEditorSettings.tsx new file mode 100644 index 0000000..cabeab5 --- /dev/null +++ b/StatusHistoryChart/src/StatusHistoryChartOptionsEditorSettings.tsx @@ -0,0 +1,58 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { LegendOptionsEditor, LegendOptionsEditorProps } from '@perses-dev/plugin-system'; +import { produce } from 'immer'; +import { OptionsEditorGroup, OptionsEditorGrid, OptionsEditorColumn } from '@perses-dev/components'; +import { Button } from '@mui/material'; +import { ReactElement } from 'react'; +import { StatusHistoryChartOptions, StatusHistroyChartEditorProps } from './status-history-model.js'; + +export function StatusHistoryChartOptionsEditorSettings(props: StatusHistroyChartEditorProps): ReactElement { + const { onChange, value } = props; + + const handleLegendChange: LegendOptionsEditorProps['onChange'] = (newLegend) => { + // TODO (sjcobb): fix type, add position, fix glitch + onChange( + produce(value, (draft: StatusHistoryChartOptions) => { + draft.legend = newLegend; + }) + ); + }; + + return ( + + + + + + + + + + + ); +} diff --git a/StatusHistoryChart/src/StatusHistoryPanel.tsx b/StatusHistoryChart/src/StatusHistoryPanel.tsx new file mode 100644 index 0000000..767716c --- /dev/null +++ b/StatusHistoryChart/src/StatusHistoryPanel.tsx @@ -0,0 +1,85 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Box } from '@mui/material'; +import { ContentWithLegend, LoadingOverlay, StatusHistoryChart, useChartsTheme } from '@perses-dev/components'; +import { PanelProps, useDataQueries, validateLegendSpec } from '@perses-dev/plugin-system'; +import { merge } from 'lodash'; +import { ReactElement, useMemo } from 'react'; +import { useStatusHistoryDataModel } from './utils/data-transform'; +import { StatusHistoryChartOptions } from './status-history-model.js'; + +export type StatusHistoryChartPanelProps = PanelProps; + +export function StatusHistoryPanel(props: StatusHistoryChartPanelProps): ReactElement | null { + const { spec, contentDimensions } = props; + + const legend = useMemo(() => { + return spec.legend && validateLegendSpec(spec.legend) ? merge({}, spec.legend) : undefined; + }, [spec.legend]); + + const chartsTheme = useChartsTheme(); + const PADDING = chartsTheme.container.padding.default; + const { queryResults, isLoading, isFetching } = useDataQueries('TimeSeriesQuery'); + + const { statusHistoryData, yAxisCategories, xAxisCategories, legendItems, timeScale } = useStatusHistoryDataModel( + queryResults, + chartsTheme.echartsTheme.color as string[] + ); + + const adjustedContentDimensions: typeof contentDimensions = contentDimensions + ? { + width: contentDimensions.width - PADDING * 2, + height: contentDimensions.height - PADDING * 2, + } + : undefined; + + if (!statusHistoryData || statusHistoryData.length === 0) { + return null; + } + + if (isLoading || isFetching) { + return ; + } + return ( + + null, + } + } + > + {({ height, width }) => { + return ( + + + + ); + }} + + + ); +} diff --git a/StatusHistoryChart/src/bootstrap.tsx b/StatusHistoryChart/src/bootstrap.tsx new file mode 100644 index 0000000..3380d4a --- /dev/null +++ b/StatusHistoryChart/src/bootstrap.tsx @@ -0,0 +1,18 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +const root = ReactDOM.createRoot(document.getElementById('root')!); +root.render(); diff --git a/StatusHistoryChart/src/env.d.ts b/StatusHistoryChart/src/env.d.ts new file mode 100644 index 0000000..b302dd9 --- /dev/null +++ b/StatusHistoryChart/src/env.d.ts @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// diff --git a/StatusHistoryChart/src/index.tsx b/StatusHistoryChart/src/index.tsx new file mode 100644 index 0000000..d3d1d92 --- /dev/null +++ b/StatusHistoryChart/src/index.tsx @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import('./bootstrap'); diff --git a/StatusHistoryChart/src/setup-tests.ts b/StatusHistoryChart/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/StatusHistoryChart/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/BarChart/src/model.ts b/StatusHistoryChart/src/status-history-model.ts similarity index 56% rename from BarChart/src/model.ts rename to StatusHistoryChart/src/status-history-model.ts index bd99ffa..d96e872 100644 --- a/BarChart/src/model.ts +++ b/StatusHistoryChart/src/status-history-model.ts @@ -11,22 +11,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CalculationType, FormatOptions } from '@perses-dev/core'; +import { LegendSpecOptions, OptionsEditorProps } from '@perses-dev/plugin-system'; -export interface BarChartOptions { - calculation: CalculationType; - format?: FormatOptions; - sort?: SortOption; - mode?: ModeOption; +export function createInitialStatusHistoryChartOptions(): Record { + return {}; } -export type SortOption = 'asc' | 'desc'; -export type ModeOption = 'value' | 'percentage'; - -export const DEFAULT_SORT: SortOption = 'desc'; -export const DEFAULT_MODE: ModeOption = 'value'; - -export interface BarChartData { - label: string; - value: number | null; +export interface StatusHistoryChartOptions { + legend?: LegendSpecOptions; } + +export type StatusHistroyChartEditorProps = OptionsEditorProps; diff --git a/StatusHistoryChart/src/utils/data-transform.test.ts b/StatusHistoryChart/src/utils/data-transform.test.ts new file mode 100644 index 0000000..f004d55 --- /dev/null +++ b/StatusHistoryChart/src/utils/data-transform.test.ts @@ -0,0 +1,73 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { TimeSeriesData } from '@perses-dev/core'; +import { QueryData } from '@perses-dev/plugin-system'; +import { renderHook } from '@testing-library/react'; +import { useStatusHistoryDataModel } from './data-transform'; + +describe('useStatusHistoryDataModel', () => { + it('should return empty model for empty query results', () => { + const { result } = renderHook(() => useStatusHistoryDataModel([], [])); + expect(result.current).toEqual({ + legendItems: [], + statusHistoryData: [], + xAxisCategories: [], + yAxisCategories: [], + }); + }); + + it('should process query results correctly', () => { + const queryResults: Array> = [ + { + data: { + timeRange: { + start: new Date(1609459200000), + end: new Date(1609459260000), + }, + stepMs: 60000, + series: [ + { + name: 'instance1', + formattedName: 'instance1', + values: [ + [1609459200000, 1], + [1609459260000, 2], + ], + + // stepMs?: number; + }, + ], + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + definition: { query: 'some-query' } as any, + error: undefined, + isFetching: false, + isLoading: false, + }, + ]; + const colors = ['#ff0000', '#00ff00']; + const { result } = renderHook(() => useStatusHistoryDataModel(queryResults, colors)); + + expect(result.current.legendItems).toEqual([ + { id: '0-1', label: '1', color: '#ff0000' }, + { id: '1-2', label: '2', color: '#00ff00' }, + ]); + expect(result.current.statusHistoryData).toEqual([ + [0, 0, 1], + [1, 0, 2], + ]); + expect(result.current.xAxisCategories).toEqual([1609459200000, 1609459260000]); + expect(result.current.yAxisCategories).toEqual(['instance1']); + }); +}); diff --git a/StatusHistoryChart/src/utils/data-transform.ts b/StatusHistoryChart/src/utils/data-transform.ts new file mode 100644 index 0000000..ad5b4a2 --- /dev/null +++ b/StatusHistoryChart/src/utils/data-transform.ts @@ -0,0 +1,101 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { getColorForValue, LegendItem } from '@perses-dev/components'; +import { TimeScale, TimeSeriesData } from '@perses-dev/core'; +import { QueryData } from '@perses-dev/plugin-system'; +import { useMemo } from 'react'; +import { getCommonTimeScaleForQueries } from './get-timescale'; + +interface StatusHistoryDataModel { + legendItems: LegendItem[]; + statusHistoryData: Array<[number, number, number | undefined]>; + xAxisCategories: number[]; + yAxisCategories: string[]; + timeScale?: TimeScale; +} + +function generateCompleteTimestamps(timescale?: TimeScale): number[] { + if (!timescale) { + return []; + } + const { startMs, endMs, stepMs } = timescale; + const timestamps: number[] = []; + for (let time = startMs; time <= endMs; time += stepMs) { + timestamps.push(time); + } + return timestamps; +} + +export function useStatusHistoryDataModel( + queryResults: Array>, + colors: string[] +): StatusHistoryDataModel { + return useMemo(() => { + if (!queryResults || queryResults.length === 0) { + return { + legendItems: [], + statusHistoryData: [], + xAxisCategories: [], + yAxisCategories: [], + }; + } + + const timeScale = getCommonTimeScaleForQueries(queryResults); + const statusHistoryData: Array<[number, number, number]> = []; + const yAxisCategories: string[] = []; + const legendSet = new Set(); + + const xAxisCategories = generateCompleteTimestamps(timeScale); + + queryResults.forEach(({ data }) => { + if (!data) { + return; + } + + data.series.forEach((item) => { + const instance = item.formattedName || ''; + + yAxisCategories.push(instance); + + const yIndex = yAxisCategories.length - 1; + + item.values.forEach(([time, value]) => { + const itemIndexOnXaxis = xAxisCategories.findIndex((v) => v === time); + + if (value !== null && itemIndexOnXaxis !== -1) { + legendSet.add(value); + statusHistoryData.push([itemIndexOnXaxis, yIndex, value]); + } + }); + }); + }); + + const legendItems: LegendItem[] = Array.from(legendSet).map((value, idx) => { + const color = colors[idx] || getColorForValue(value, colors[0] || '#1f77b4'); + return { + id: `${idx}-${value}`, + label: String(value), + color, + }; + }); + + return { + xAxisCategories, + yAxisCategories, + legendItems, + statusHistoryData, + timeScale, + }; + }, [queryResults, colors]); +} diff --git a/StatusHistoryChart/src/utils/get-timescale.ts b/StatusHistoryChart/src/utils/get-timescale.ts new file mode 100644 index 0000000..a29f4f4 --- /dev/null +++ b/StatusHistoryChart/src/utils/get-timescale.ts @@ -0,0 +1,22 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { getCommonTimeScale, TimeScale, TimeSeriesData } from '@perses-dev/core'; +import { UseDataQueryResults } from '@perses-dev/plugin-system'; + +export function getCommonTimeScaleForQueries( + queries: UseDataQueryResults['queryResults'] +): TimeScale | undefined { + const seriesData = queries.map((query) => (query.isLoading ? undefined : query.data)); + return getCommonTimeScale(seriesData); +} diff --git a/StatusHistoryChart/tsconfig.json b/StatusHistoryChart/tsconfig.json new file mode 100644 index 0000000..da01ddb --- /dev/null +++ b/StatusHistoryChart/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.base.json" +} \ No newline at end of file diff --git a/Table/README.md b/Table/README.md new file mode 100644 index 0000000..a4a8709 --- /dev/null +++ b/Table/README.md @@ -0,0 +1,23 @@ +# Perses Panel Plugin + +## Setup + +Install dependencies: + +```bash +npm install +``` + +## Get Started + +Start the dev server: + +```bash +npm run dev +``` + +Build the plugin for distribution: + +```bash +npm run build +``` diff --git a/Table/jest.config.ts b/Table/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/Table/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/Table/package.json b/Table/package.json new file mode 100644 index 0000000..a66b34c --- /dev/null +++ b/Table/package.json @@ -0,0 +1,46 @@ +{ + "name": "@perses-dev/table", + "private": true, + "version": "0.4.0", + "scripts": { + "dev": "rsbuild dev", + "build": "rsbuild build", + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@module-federation/enhanced": "^0.1.11", + "@perses-dev/components": "^0.50.0-rc.1", + "@perses-dev/core": "^0.50.0-rc.1", + "@perses-dev/plugin-system": "^0.50.0-rc.1" + }, + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0" + }, + "files": [ + "dist" + ], + "perses": { + "plugins": [ + { + "kind": "Panel", + "spec": { + "display": { + "name": "Table" + }, + "name": "Table" + } + } + ] + } +} diff --git a/Table/rsbuild.config.ts b/Table/rsbuild.config.ts new file mode 100644 index 0000000..99d8a07 --- /dev/null +++ b/Table/rsbuild.config.ts @@ -0,0 +1,61 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack'; +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; + +export default defineConfig({ + server: { + port: 3005, + }, + dev: { + assetPrefix: '/plugins/Table/', + }, + output: { + assetPrefix: '/plugins/Table/', + copy: [{ from: './package.json' }, { from: 'README.md' }], + }, + plugins: [pluginReact()], + tools: { + htmlPlugin: false, + rspack: (config, { appendPlugins }) => { + config.output!.uniqueName = 'Table'; + appendPlugins([ + new ModuleFederationPlugin({ + name: 'Table', + exposes: { + './Table': './src/Table.ts', + }, + shared: { + react: { requiredVersion: '18.2.0', singleton: true }, + 'react-dom': { requiredVersion: '18.2.0', singleton: true }, + echarts: { singleton: true }, + 'date-fns': { singleton: true }, + 'date-fns-tz': { singleton: true }, + lodash: { singleton: true }, + '@perses-dev/components': { singleton: true }, + '@perses-dev/plugin-system': { singleton: true }, + '@emotion/react': { requiredVersion: '^11.11.3', singleton: true }, + '@emotion/styled': { singleton: true }, + '@hookform/resolvers': { singleton: true }, + '@tanstack/react-query': { singleton: true }, + 'react-hook-form': { singleton: true }, + }, + dts: false, + runtime: false, + }), + ]); + }, + }, +}); diff --git a/Table/src/CellsEditor/CellEditor.tsx b/Table/src/CellsEditor/CellEditor.tsx new file mode 100644 index 0000000..6230724 --- /dev/null +++ b/Table/src/CellsEditor/CellEditor.tsx @@ -0,0 +1,235 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { + Grid2Props as GridProps, + IconButton, + MenuItem, + Stack, + StackProps, + TextField, + Tooltip, + Typography, + Grid2 as Grid, +} from '@mui/material'; +import DeleteIcon from 'mdi-material-ui/DeleteOutline'; +import { OptionsColorPicker } from '@perses-dev/components'; +import PlusIcon from 'mdi-material-ui/Plus'; +import { ReactElement } from 'react'; +import { CellSettings, Condition } from '../table-model'; + +interface ConditionEditorProps extends Omit { + condition: Condition; + onChange: (condition: Condition) => void; +} + +function ConditionEditor({ condition, onChange, ...props }: ConditionEditorProps): ReactElement | null { + if (condition.kind === 'Value') { + return ( + + onChange({ ...condition, spec: { value: e.target.value } } as Condition)} + fullWidth + /> + + ); + } else if (condition.kind === 'Range') { + return ( + + onChange({ ...condition, spec: { ...condition.spec, min: +e.target.value } } as Condition)} + fullWidth + /> + onChange({ ...condition, spec: { ...condition.spec, max: +e.target.value } } as Condition)} + fullWidth + /> + + ); + } else if (condition.kind === 'Regex') { + return ( + + onChange({ ...condition, spec: { expr: e.target.value } } as Condition)} + fullWidth + /> + + ); + } else if (condition.kind === 'Misc') { + return ( + + onChange({ ...condition, spec: { value: e.target.value } } as Condition)} + fullWidth + > + + + Empty + Matches empty string + + + + + Null + Matches null or undefined + + + + + NaN + Matches Not a Number value + + + + + True + Matches true boolean + + + + + False + Matches false boolean + + + + + ); + } + return null; +} + +export interface CellEditorProps extends Omit { + cell: CellSettings; + onChange: (cell: CellSettings) => void; + onDelete: () => void; +} + +export function CellEditor({ cell, onChange, onDelete, ...props }: CellEditorProps): ReactElement { + return ( + + + + onChange({ ...cell, condition: { kind: e.target.value } } as CellSettings)} + required + sx={{ width: '120px' }} + > + + + Value + {cell.condition.kind !== 'Value' && ( + Matches an exact text value + )} + + + + + Range + {cell.condition.kind !== 'Range' && ( + Matches against a numerical range + )} + + + + + Regex + {cell.condition.kind !== 'Regex' && ( + Matches against a regular expression + )} + + + + + Misc + {cell.condition.kind !== 'Misc' && ( + Matches against empty, null and NaN values + )} + + + + onChange({ ...cell, condition: updatedCondition })} + /> + + + + onChange({ ...cell, text: e.target.value })} + fullWidth + /> + + + + {cell.textColor ? ( + onChange({ ...cell, textColor: color } as CellSettings)} + onClear={() => onChange({ ...cell, textColor: undefined } as CellSettings)} + /> + ) : ( + onChange({ ...cell, textColor: '#000' })}> + + + )} + + + + + {cell.backgroundColor ? ( + onChange({ ...cell, backgroundColor: color } as CellSettings)} + onClear={() => onChange({ ...cell, backgroundColor: undefined } as CellSettings)} + /> + ) : ( + onChange({ ...cell, backgroundColor: '#000' })}> + + + )} + + + + + + + + + + + ); +} diff --git a/Table/src/CellsEditor/CellsEditor.tsx b/Table/src/CellsEditor/CellsEditor.tsx new file mode 100644 index 0000000..e706970 --- /dev/null +++ b/Table/src/CellsEditor/CellsEditor.tsx @@ -0,0 +1,78 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Button, Divider, Stack, Typography, Grid2 as Grid } from '@mui/material'; + +import AddIcon from 'mdi-material-ui/Plus'; +import { ReactElement } from 'react'; +import { CellSettings } from '../table-model'; +import { CellEditor } from './CellEditor'; + +export interface CellsEditorProps { + cellSettings: CellSettings[]; + onChange: (cellOptions: CellSettings[]) => void; +} + +export function CellsEditor({ cellSettings, onChange }: CellsEditorProps): ReactElement { + function handleCellChange(index: number, cell: CellSettings): void { + const updatedCells = [...cellSettings]; + updatedCells[index] = cell; + onChange(updatedCells); + } + + function handleAddCellEditor(): void { + const updatedCells = [...cellSettings]; + updatedCells.push({ condition: { kind: 'Value', spec: { value: '' } } }); + onChange(updatedCells); + } + + function handleCellDelete(index: number): void { + const updatedCells = [...cellSettings]; + updatedCells.splice(index, 1); + onChange(updatedCells); + } + + return ( + + + + Condition + + + Display Text + + + Color + + + Background + + + + }> + {cellSettings.map((cell, i) => ( + handleCellChange(i, updatedCell)} + onDelete={() => handleCellDelete(i)} + /> + ))} + + + + + ); +} diff --git a/Table/src/CellsEditor/index.ts b/Table/src/CellsEditor/index.ts new file mode 100644 index 0000000..a658a88 --- /dev/null +++ b/Table/src/CellsEditor/index.ts @@ -0,0 +1,15 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export * from './CellEditor'; +export * from './CellsEditor'; diff --git a/Table/src/ColumnsEditor/ColumnEditor.tsx b/Table/src/ColumnsEditor/ColumnEditor.tsx new file mode 100644 index 0000000..c998e66 --- /dev/null +++ b/Table/src/ColumnsEditor/ColumnEditor.tsx @@ -0,0 +1,159 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Divider, FormControlLabel, Stack, StackProps, Switch, TextField } from '@mui/material'; +import { ReactElement, useState } from 'react'; +import { AlignSelector, SortSelectorButtons } from '@perses-dev/components'; +import { ColumnSettings } from '../table-model'; + +type OmittedMuiProps = 'children' | 'value' | 'onChange'; + +export interface ColumnEditorProps extends Omit { + column: ColumnSettings; + onChange: (column: ColumnSettings) => void; +} + +export function ColumnEditor({ column, onChange, ...others }: ColumnEditorProps): ReactElement { + const [width, setWidth] = useState( + column.width === undefined || column.width === 'auto' ? 100 : column.width + ); + + return ( + + + onChange({ ...column, name: e.target.value })} + required + /> + + + + onChange({ ...column, header: e.target.value ? e.target.value : undefined })} + /> + onChange({ ...column, headerDescription: e.target.value ? e.target.value : undefined })} + /> + onChange({ ...column, cellDescription: e.target.value ? e.target.value : undefined })} + /> + + + + + + + onChange({ ...column, hide: e.target.checked })} + /> + } + /> + onChange({ ...column, align: align })} + /> + } + /> + + + + onChange({ ...column, enableSorting: e.target.checked })} + /> + } + /> + + onChange({ ...column, sort: sort })} + /> + } + /> + + + + onChange({ ...column, width: e.target.checked ? width : 'auto' })} + /> + } + /> + { + setWidth(+e.target.value); + onChange({ ...column, width: +e.target.value }); + }} + /> + + + + ); +} diff --git a/Table/src/ColumnsEditor/ColumnEditorContainer.tsx b/Table/src/ColumnsEditor/ColumnEditorContainer.tsx new file mode 100644 index 0000000..30c0d51 --- /dev/null +++ b/Table/src/ColumnsEditor/ColumnEditorContainer.tsx @@ -0,0 +1,105 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Divider, IconButton, Stack, Tooltip, Typography } from '@mui/material'; +import ChevronRight from 'mdi-material-ui/ChevronRight'; +import ChevronDown from 'mdi-material-ui/ChevronDown'; +import DeleteIcon from 'mdi-material-ui/DeleteOutline'; +import EyeIcon from 'mdi-material-ui/EyeOutline'; +import EyeOffIcon from 'mdi-material-ui/EyeOffOutline'; +import { DragAndDropElement, DragButton } from '@perses-dev/components'; +import { ReactElement } from 'react'; +import { ColumnEditor, ColumnEditorProps } from './ColumnEditor'; + +export interface ColumnEditorContainerProps extends ColumnEditorProps { + isCollapsed: boolean; + onCollapse: (collapsed: boolean) => void; + onDelete: () => void; + onMoveUp: () => void; + onMoveDown: () => void; +} + +export function ColumnEditorContainer({ + column, + isCollapsed, + onChange, + onCollapse, + onDelete, + onMoveUp, + onMoveDown, +}: ColumnEditorContainerProps): ReactElement { + function handleHideColumn(): void { + onChange({ ...column, hide: !column.hide }); + } + + return ( + }> + theme.palette.divider} + justifyContent="space-between" + gap={4} + > + + onCollapse(!isCollapsed)} + > + {isCollapsed ? : } + + + COLUMN: + {column.header ? ( + + {column.header} ({column.name}) + + ) : ( + {column.name} + )} + + + + + {isCollapsed && ( + <> + + + {column.hide ? : } + + + + + )} + + + + + + + theme.palette.background.lighter }, + }} + /> + + + + {!isCollapsed && } + + ); +} diff --git a/Table/src/ColumnsEditor/ColumnsEditor.tsx b/Table/src/ColumnsEditor/ColumnsEditor.tsx new file mode 100644 index 0000000..7115190 --- /dev/null +++ b/Table/src/ColumnsEditor/ColumnsEditor.tsx @@ -0,0 +1,89 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Button, Stack } from '@mui/material'; +import { ReactElement, useState } from 'react'; +import AddIcon from 'mdi-material-ui/Plus'; +import { handleMoveDown, handleMoveUp, useDragAndDropMonitor } from '@perses-dev/components'; +import { ColumnSettings } from '../table-model'; +import { ColumnEditorContainer } from './ColumnEditorContainer'; + +export interface ColumnsEditorProps { + columnSettings: ColumnSettings[]; + onChange: (columnOptions: ColumnSettings[]) => void; +} + +export function ColumnsEditor({ columnSettings, onChange }: ColumnsEditorProps): ReactElement { + const [columnsCollapsed, setColumnsCollapsed] = useState(columnSettings.map(() => true)); + + function handleColumnChange(index: number, column: ColumnSettings): void { + const updatedColumns = [...columnSettings]; + updatedColumns[index] = column; + onChange(updatedColumns); + } + + function handleColumnAdd(): void { + const columnName: string = `column_${Object.keys(columnSettings).length}`; + const updatedColumns = [...columnSettings]; + updatedColumns.push({ name: columnName }); + onChange(updatedColumns); + setColumnsCollapsed((prev) => { + prev.push(false); + return [...prev]; + }); + } + + function handleColumnDelete(index: number): void { + const updatedColumns = [...columnSettings]; + updatedColumns.splice(index, 1); + onChange(updatedColumns); + setColumnsCollapsed((prev) => { + prev.splice(index, 1); + return [...prev]; + }); + } + + function handleColumnCollapseExpand(index: number, collapsed: boolean): void { + setColumnsCollapsed((prev) => { + prev[index] = collapsed; + return [...prev]; + }); + } + + useDragAndDropMonitor({ + elements: columnSettings as unknown as Array>, + accessKey: 'name', + onChange: onChange as unknown as (elements: Array>) => void, + }); + + return ( + + {columnSettings.map((column, i) => ( + handleColumnChange(i, updatedColumn)} + onDelete={() => handleColumnDelete(i)} + onCollapse={(collapsed) => handleColumnCollapseExpand(i, collapsed)} + onMoveUp={() => onChange(handleMoveUp(column, columnSettings))} + onMoveDown={() => onChange(handleMoveDown(column, columnSettings))} + /> + ))} + + + + ); +} diff --git a/Table/src/ColumnsEditor/index.ts b/Table/src/ColumnsEditor/index.ts new file mode 100644 index 0000000..10b09c0 --- /dev/null +++ b/Table/src/ColumnsEditor/index.ts @@ -0,0 +1,16 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export * from './ColumnEditor'; +export * from './ColumnEditorContainer'; +export * from './ColumnsEditor'; diff --git a/Table/src/Table.ts b/Table/src/Table.ts new file mode 100644 index 0000000..98839af --- /dev/null +++ b/Table/src/Table.ts @@ -0,0 +1,38 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin } from '@perses-dev/plugin-system'; +import { createInitialTableOptions, TableOptions } from './table-model'; +import { TablePanel } from './TablePanel'; +import { TableColumnsEditor } from './TableColumnsEditor'; +import { TableSettingsEditor } from './TableSettingsEditor'; +import { TableCellsEditor } from './TableCellsEditor'; +import { TableTransformsEditor } from './TableTransformsEditor'; + +/** + * The core TimeSeriesTable panel plugin for Perses. + */ +export const Table: PanelPlugin = { + PanelComponent: TablePanel, + supportedQueryTypes: ['TimeSeriesQuery'], + queryOptions: { + mode: 'instant', + }, + panelOptionsEditorComponents: [ + { label: 'General Settings', content: TableSettingsEditor }, + { label: 'Column Settings', content: TableColumnsEditor }, + { label: 'Cell Settings', content: TableCellsEditor }, + { label: 'Transforms', content: TableTransformsEditor }, + ], + createInitialOptions: createInitialTableOptions, +}; diff --git a/Table/src/TableCellsEditor.tsx b/Table/src/TableCellsEditor.tsx new file mode 100644 index 0000000..0a54c44 --- /dev/null +++ b/Table/src/TableCellsEditor.tsx @@ -0,0 +1,27 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { OptionsEditorProps } from '@perses-dev/plugin-system'; +import { ReactElement } from 'react'; +import { CellsEditor } from './CellsEditor'; +import { CellSettings, TableOptions } from './table-model'; + +export type TableCellsEditorProps = OptionsEditorProps; + +export function TableCellsEditor({ onChange, value }: TableCellsEditorProps): ReactElement { + function handleCellsChange(cells: CellSettings[]): void { + onChange({ ...value, cellSettings: cells }); + } + + return ; +} diff --git a/Table/src/TableColumnsEditor.test.tsx b/Table/src/TableColumnsEditor.test.tsx new file mode 100644 index 0000000..96f0529 --- /dev/null +++ b/Table/src/TableColumnsEditor.test.tsx @@ -0,0 +1,55 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { fireEvent, render, screen } from '@testing-library/react'; +import { TableColumnsEditor } from './TableColumnsEditor'; +import { TableOptions } from './table-model'; + +describe('TableColumnsEditor', () => { + function renderTableColumnsEditor(value: TableOptions, onChange = jest.fn()): void { + render(); + } + + it('can add a new column settings', () => { + const onChange = jest.fn(); + renderTableColumnsEditor({ columnSettings: [] }, onChange); + const addColumnButton = screen.getByRole('button', { name: /Add Column Settings/i }); + fireEvent.click(addColumnButton); + expect(onChange).toHaveBeenCalledWith({ columnSettings: [{ name: 'column_0' }] }); + }); + + it('can enable column custom width', () => { + const onChange = jest.fn(); + renderTableColumnsEditor({ columnSettings: [{ name: 'column_0' }] }, onChange); + + // Expand the column settings editor for column_0 + const collapseIcon = screen.getByTestId('column-toggle#column_0'); + fireEvent.click(collapseIcon); + const customWidthSwitch = screen.getByRole('checkbox', { name: /Custom width/i }); + fireEvent.click(customWidthSwitch); + expect(onChange).toHaveBeenCalledWith({ columnSettings: [{ name: 'column_0', width: 100 }] }); + }); + + it('can rename a column', () => { + const onChange = jest.fn(); + renderTableColumnsEditor({ columnSettings: [{ name: 'column_0' }] }, onChange); + + // Expand the column settings editor for column_0 + const collapseIcon = screen.getByTestId('column-toggle#column_0'); + fireEvent.click(collapseIcon); + + const nameInput = screen.getByRole('textbox', { name: /Name/i }); + fireEvent.change(nameInput, { target: { value: 'MySuperName' } }); + expect(onChange).toHaveBeenCalledWith({ columnSettings: [{ name: 'MySuperName' }] }); + }); +}); diff --git a/Table/src/TableColumnsEditor.tsx b/Table/src/TableColumnsEditor.tsx new file mode 100644 index 0000000..165c1d1 --- /dev/null +++ b/Table/src/TableColumnsEditor.tsx @@ -0,0 +1,27 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { OptionsEditorProps } from '@perses-dev/plugin-system'; +import { ReactElement } from 'react'; +import { ColumnSettings, TableOptions } from './table-model'; +import { ColumnsEditor } from './ColumnsEditor'; + +export type TableColumnsEditorProps = OptionsEditorProps; + +export function TableColumnsEditor({ onChange, value }: TableColumnsEditorProps): ReactElement { + function handleColumnsChange(columns: ColumnSettings[]): void { + onChange({ ...value, columnSettings: columns }); + } + + return ; +} diff --git a/Table/src/TablePanel.test.tsx b/Table/src/TablePanel.test.tsx new file mode 100644 index 0000000..a4bc77e --- /dev/null +++ b/Table/src/TablePanel.test.tsx @@ -0,0 +1,169 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ChartsProvider, testChartsTheme } from '@perses-dev/components'; +import { TimeRangeValue, TimeSeriesData, toAbsoluteTimeRange, UnknownSpec } from '@perses-dev/core'; +import { + MockPlugin, + mockPluginRegistry, + PluginRegistry, + TimeRangeContext, + TimeSeriesQueryPlugin, + useDataQueries, +} from '@perses-dev/plugin-system'; +import { render, screen, within } from '@testing-library/react'; +import { ReactElement } from 'react'; +import { VirtuosoMockContext } from 'react-virtuoso'; +import { TimeSeriesTableProps } from './model'; +import { TableOptions } from './table-model'; +import { TablePanel } from './TablePanel'; +import { + MOCK_TIME_SERIES_DATA_SINGLEVALUE, + MOCK_TIME_SERIES_QUERY_RESULT_SINGLEVALUE, +} from './test/mock-query-results'; + +jest.mock('@perses-dev/plugin-system', () => { + return { + ...jest.requireActual('@perses-dev/plugin-system'), + useDataQueries: jest.fn(), + }; +}); + +function buildFakeTimeSeriesQuery(data: TimeSeriesData): TimeSeriesQueryPlugin { + return { + getTimeSeriesData: async (): Promise => { + return data; + }, + OptionsEditorComponent: (): ReactElement => { + return
    Edit options here
    ; + }, + createInitialOptions: () => ({}), + }; +} + +function buildMockQueryPlugin(data: TimeSeriesData): MockPlugin { + return { + pluginType: 'TimeSeriesQuery', + kind: 'PrometheusTimeSeriesQuery', + plugin: buildFakeTimeSeriesQuery(data), + }; +} + +const TEST_TIME_RANGE: TimeRangeValue = { pastDuration: '1h' }; + +const TEST_TIME_SERIES_TABLE_PROPS: TimeSeriesTableProps = { + contentDimensions: { + width: 500, + height: 500, + }, + spec: {}, +}; + +describe('TablePanel', () => { + // Helper to render the panel with some context set + const renderPanel = (data: TimeSeriesData, options?: TableOptions): void => { + const mockTimeRangeContext = { + refreshIntervalInMs: 0, + setRefreshInterval: (): Record => ({}), + timeRange: TEST_TIME_RANGE, + setTimeRange: (): Record => ({}), + absoluteTimeRange: toAbsoluteTimeRange(TEST_TIME_RANGE), + refresh: jest.fn(), + refreshKey: `${TEST_TIME_RANGE.pastDuration}:0`, + }; + + render( + + + + + + + + + + ); + }; + + it('should render time series in table', async () => { + (useDataQueries as jest.Mock).mockReturnValue({ + queryResults: MOCK_TIME_SERIES_QUERY_RESULT_SINGLEVALUE, + isLoading: false, + isFetching: false, + }); + renderPanel(MOCK_TIME_SERIES_DATA_SINGLEVALUE); + + expect(await screen.findAllByRole('columnheader')).toHaveLength(8); // 1 timestamp column + 1 value column + 6 labels columns + expect(await screen.findByRole('columnheader', { name: 'timestamp' })).toBeInTheDocument(); + expect(await screen.findByRole('columnheader', { name: 'value' })).toBeInTheDocument(); + expect(await screen.findByRole('columnheader', { name: 'device' })).toBeInTheDocument(); + expect(await screen.findByRole('columnheader', { name: 'env' })).toBeInTheDocument(); + expect(await screen.findByRole('columnheader', { name: 'fstype' })).toBeInTheDocument(); + expect(await screen.findByRole('columnheader', { name: 'instance' })).toBeInTheDocument(); + expect(await screen.findByRole('columnheader', { name: 'job' })).toBeInTheDocument(); + expect(await screen.findByRole('columnheader', { name: 'mountpoint' })).toBeInTheDocument(); + + expect(await screen.findAllByRole('cell')).toHaveLength(16); // 2 time series with 8 columns + }, 15000); // Github Actions is slow + + it('should apply column settings', async () => { + (useDataQueries as jest.Mock).mockReturnValue({ + queryResults: MOCK_TIME_SERIES_QUERY_RESULT_SINGLEVALUE, + isLoading: false, + isFetching: false, + }); + renderPanel(MOCK_TIME_SERIES_DATA_SINGLEVALUE, { + columnSettings: [ + { name: 'value', header: 'Value', headerDescription: 'Timeseries Value' }, + { name: 'device', width: 200 }, + { name: 'env', hide: true }, + { name: 'fstype', enableSorting: true }, + ], + }); + + expect(await screen.findAllByRole('columnheader')).toHaveLength(7); // 1 timestamp column + 1 value column + 6 labels columns - 1 column hidden + expect(screen.queryByRole('columnheader', { name: 'env' })).not.toBeInTheDocument(); + + const valueHeaderCell = await screen.findByRole('columnheader', { name: /Value/i }); + expect(valueHeaderCell).toBeInTheDocument(); + expect(await within(valueHeaderCell).findByLabelText('Timeseries Value')).toBeInTheDocument(); + expect(await screen.findByRole('columnheader', { name: /Value/i })).toBeInTheDocument(); + + const fstypeHeaderCell = await screen.findByRole('columnheader', { name: 'fstype' }); + expect(fstypeHeaderCell).toBeInTheDocument(); + expect(await within(fstypeHeaderCell).findByTestId('ArrowDownwardIcon')).toBeInTheDocument(); + + expect(await screen.findAllByRole('cell')).toHaveLength(14); // 2 time series with 7 columns + }); + + it('should apply transforms', async () => { + (useDataQueries as jest.Mock).mockReturnValue({ + queryResults: MOCK_TIME_SERIES_QUERY_RESULT_SINGLEVALUE, + isLoading: false, + isFetching: false, + }); + renderPanel(MOCK_TIME_SERIES_DATA_SINGLEVALUE, { + transforms: [ + { + kind: 'JoinByColumnValue', + spec: { + columns: ['env'], + }, + }, + ], + }); + + expect(await screen.findAllByRole('cell')).toHaveLength(8); // 1 row of 8 columns (not joined => 16) + expect(await screen.findByRole('cell', { name: 'demo' })).toBeInTheDocument(); + }); +}); diff --git a/Table/src/TablePanel.tsx b/Table/src/TablePanel.tsx new file mode 100644 index 0000000..53742e6 --- /dev/null +++ b/Table/src/TablePanel.tsx @@ -0,0 +1,252 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelProps, QueryData, useDataQueries } from '@perses-dev/plugin-system'; +import { LoadingOverlay, Table, TableCellConfig, TableCellConfigs, TableColumnConfig } from '@perses-dev/components'; +import { ReactElement, useMemo, useState } from 'react'; +import { Labels, TimeSeries, TimeSeriesData, useTransformData } from '@perses-dev/core'; +import { SortingState } from '@tanstack/react-table'; +import { CellSettings, ColumnSettings, TableOptions } from './table-model'; + +/* + * Generate column config from column definitions, if a column has multiple definitions, the first one will be used. + * If column is hidden, return undefined. + * If column do not have a definition, return a default column config. + */ +function generateColumnConfig(name: string, columnSettings: ColumnSettings[]): TableColumnConfig | undefined { + for (const column of columnSettings) { + if (column.name === name) { + if (column.hide) { + return undefined; + } + + return { + accessorKey: name, + header: column.header ?? name, + headerDescription: column.headerDescription, + cellDescription: column.cellDescription ? (_): string => `${column.cellDescription}` : undefined, // TODO: variable rendering + cell value + enableSorting: column.enableSorting, + width: column.width, + align: column.align, + }; + } + } + + return { + accessorKey: name, + header: name, + }; +} + +function generateCellConfig(value: unknown, settings: CellSettings[]): TableCellConfig | undefined { + for (const setting of settings) { + if (setting.condition.kind === 'Value' && setting.condition.spec?.value === String(value)) { + return { text: setting.text, textColor: setting.textColor, backgroundColor: setting.backgroundColor }; + } + + if (setting.condition.kind === 'Range' && !Number.isNaN(Number(value))) { + const numericValue = Number(value); + if ( + setting.condition.spec?.min !== undefined && + setting.condition.spec?.max !== undefined && + numericValue >= +setting.condition.spec?.min && + numericValue <= +setting.condition.spec?.max + ) { + return { text: setting.text, textColor: setting.textColor, backgroundColor: setting.backgroundColor }; + } + + if (setting.condition.spec?.min !== undefined && numericValue >= +setting.condition.spec?.min) { + return { text: setting.text, textColor: setting.textColor, backgroundColor: setting.backgroundColor }; + } + + if (setting.condition.spec?.max !== undefined && numericValue <= +setting.condition.spec?.max) { + return { text: setting.text, textColor: setting.textColor, backgroundColor: setting.backgroundColor }; + } + } + + if (setting.condition.kind === 'Regex' && setting.condition.spec?.expr) { + const regex = new RegExp(setting.condition.spec?.expr); + if (regex.test(String(value))) { + return { text: setting.text, textColor: setting.textColor, backgroundColor: setting.backgroundColor }; + } + } + + if (setting.condition.kind === 'Misc' && setting.condition.spec?.value) { + if (setting.condition.spec?.value === 'empty' && value === '') { + return { text: setting.text, textColor: setting.textColor, backgroundColor: setting.backgroundColor }; + } + if (setting.condition.spec?.value === 'null' && (value === null || value === undefined)) { + return { text: setting.text, textColor: setting.textColor, backgroundColor: setting.backgroundColor }; + } + if (setting.condition.spec?.value === 'NaN' && Number.isNaN(value)) { + return { text: setting.text, textColor: setting.textColor, backgroundColor: setting.backgroundColor }; + } + if (setting.condition.spec?.value === 'true' && value === true) { + return { text: setting.text, textColor: setting.textColor, backgroundColor: setting.backgroundColor }; + } + if (setting.condition.spec?.value === 'false' && value === false) { + return { text: setting.text, textColor: setting.textColor, backgroundColor: setting.backgroundColor }; + } + } + } + return undefined; +} + +export type TableProps = PanelProps; + +export function TablePanel({ contentDimensions, spec }: TableProps): ReactElement | null { + // TODO: handle other query types + const { isFetching, isLoading, queryResults } = useDataQueries('TimeSeriesQuery'); + + const rawData: Array> = useMemo(() => { + return queryResults + .flatMap( + (d: QueryData, queryIndex: number) => + d.data?.series.map((ts: TimeSeries) => ({ ts, queryIndex })) || [] + ) + .map(({ ts, queryIndex }: { ts: TimeSeries; queryIndex: number }) => { + if (ts.values[0] === undefined) { + return { ...ts.labels }; + } + if (queryResults.length === 1) { + return { timestamp: ts.values[0][0], value: ts.values[0][1], ...ts.labels }; + } + + // If there is more than one query, we need to add the query index to the value key to avoid conflicts + const labels = Object.entries(ts.labels ?? {}).reduce((acc, [key, value]) => { + if (key) acc[`${key} #${queryIndex + 1}`] = value; + return acc; + }, {} as Labels); + + // If there are multiple queries, we need to add the query index to the value key to avoid conflicts + // Timestamp is not indexed as it will be the same for all queries + return { timestamp: ts.values[0][0], [`value #${queryIndex + 1}`]: ts.values[0][1], ...labels }; + }); + }, [queryResults]); + + // Transform will be applied by their orders on the original data + const data = useTransformData(rawData, spec.transforms ?? []); + + const keys: string[] = useMemo(() => { + const result: string[] = []; + + for (const entry of data) { + for (const key of Object.keys(entry)) { + if (!result.includes(key)) { + result.push(key); + } + } + } + + return result; + }, [data]); + + const columns: Array> = useMemo(() => { + const columns: Array> = []; + + // Taking the customized columns first for the ordering of the columns in the table + const customizedColumns = + spec.columnSettings?.map((column) => column.name).filter((name) => keys.includes(name)) ?? []; + const defaultColumns = keys.filter((key) => !customizedColumns.includes(key)); + + for (const key of customizedColumns) { + const columnConfig = generateColumnConfig(key, spec.columnSettings ?? []); + if (columnConfig !== undefined) { + columns.push(columnConfig); + } + } + for (const key of defaultColumns) { + const columnConfig = generateColumnConfig(key, spec.columnSettings ?? []); + if (columnConfig !== undefined) { + columns.push(columnConfig); + } + } + + return columns; + }, [keys, spec.columnSettings]); + + // Generate cell settings that will be used by the table to render cells (text color, background color, ...) + const cellConfigs: TableCellConfigs = useMemo(() => { + // If there is no cell settings, return an empty array + if (spec.cellSettings === undefined) { + return {}; + } + + const result: TableCellConfigs = {}; + + let index = 0; + for (const row of data) { + // Transforming key to object to extend the row with undefined values if the key is not present + // for checking the cell config "Misc" condition with "null" + const keysAsObj = keys.reduce( + (acc, key) => { + acc[key] = undefined; + return acc; + }, + {} as Record + ); + + const extendRow = { + ...keysAsObj, + ...row, + }; + + for (const [key, value] of Object.entries(extendRow)) { + const cellConfig = generateCellConfig(value, spec.cellSettings ?? []); + if (cellConfig) { + result[`${index}_${key}`] = cellConfig; + } + } + index++; + } + + return result; + }, [data, keys, spec.cellSettings]); + + function generateDefaultSortingState(): SortingState { + return ( + spec.columnSettings + ?.filter((column) => column.sort !== undefined) + .map((column) => { + return { + id: column.name, + desc: column.sort === 'desc', + }; + }) ?? [] + ); + } + + const [sorting, setSorting] = useState(generateDefaultSortingState()); + + if (isLoading || isFetching) { + return ; + } + + if (contentDimensions === undefined) { + return null; + } + + return ( +
  • + ); +} diff --git a/Table/src/TableSettingsEditor.tsx b/Table/src/TableSettingsEditor.tsx new file mode 100644 index 0000000..c73e529 --- /dev/null +++ b/Table/src/TableSettingsEditor.tsx @@ -0,0 +1,86 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { + DensitySelector, + OptionsEditorColumn, + OptionsEditorControl, + OptionsEditorGrid, + OptionsEditorGroup, + TableDensity, + DEFAULT_COLUMN_WIDTH, +} from '@perses-dev/components'; +import { OptionsEditorProps } from '@perses-dev/plugin-system'; +import { Switch, TextField } from '@mui/material'; +import { ChangeEvent, ReactElement } from 'react'; +import { TableOptions } from './table-model'; + +function DefaultColumnsWidthControl({ + value, + onChange, +}: { + value?: 'auto' | number; + onChange: (defaultWidth: 'auto' | number) => void; +}): ReactElement { + function handleAutoWidthChange(_: ChangeEvent, checked: boolean): void { + if (checked) { + return onChange('auto'); + } + onChange(DEFAULT_COLUMN_WIDTH); + } + + return ( + <> + } + /> + {value !== 'auto' && ( + onChange(parseInt(e.target.value))} + /> + } + /> + )} + + ); +} + +export type TableSettingsEditorProps = OptionsEditorProps; + +export function TableSettingsEditor({ onChange, value }: TableSettingsEditorProps): ReactElement { + function handleDensityChange(density: TableDensity): void { + onChange({ ...value, density: density }); + } + + function handleAutoWidthChange(newValue: 'auto' | number): void { + onChange({ ...value, defaultColumnWidth: newValue }); + } + + return ( + + + + + + + + + ); +} diff --git a/Table/src/TableTransformsEditor.tsx b/Table/src/TableTransformsEditor.tsx new file mode 100644 index 0000000..c1f1a54 --- /dev/null +++ b/Table/src/TableTransformsEditor.tsx @@ -0,0 +1,28 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { OptionsEditorProps } from '@perses-dev/plugin-system'; +import { Transform } from '@perses-dev/core'; +import { TransformsEditor } from '@perses-dev/components'; +import { ReactElement } from 'react'; +import { TableOptions } from './table-model'; + +export type TableSettingsEditorProps = OptionsEditorProps; + +export function TableTransformsEditor({ value, onChange }: TableSettingsEditorProps): ReactElement { + function handleTransformsChange(transforms: Transform[]): void { + onChange({ ...value, transforms: transforms }); + } + + return ; +} diff --git a/Table/src/bootstrap.tsx b/Table/src/bootstrap.tsx new file mode 100644 index 0000000..3380d4a --- /dev/null +++ b/Table/src/bootstrap.tsx @@ -0,0 +1,18 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +const root = ReactDOM.createRoot(document.getElementById('root')!); +root.render(); diff --git a/Table/src/env.d.ts b/Table/src/env.d.ts new file mode 100644 index 0000000..b302dd9 --- /dev/null +++ b/Table/src/env.d.ts @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// diff --git a/Table/src/index.tsx b/Table/src/index.tsx new file mode 100644 index 0000000..d3d1d92 --- /dev/null +++ b/Table/src/index.tsx @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import('./bootstrap'); diff --git a/Table/src/model.ts b/Table/src/model.ts new file mode 100644 index 0000000..f23a66b --- /dev/null +++ b/Table/src/model.ts @@ -0,0 +1,5 @@ +import { PanelProps } from '@perses-dev/plugin-system'; + +interface TimeSeriesTableOptions {} + +export type TimeSeriesTableProps = PanelProps; diff --git a/Table/src/setup-tests.ts b/Table/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/Table/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/Table/src/table-model.ts b/Table/src/table-model.ts new file mode 100644 index 0000000..39155b0 --- /dev/null +++ b/Table/src/table-model.ts @@ -0,0 +1,122 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Definition, Transform } from '@perses-dev/core'; +import { TableDensity } from '@perses-dev/components'; + +export interface ColumnSettings { + name: string; + + // Text to display in the header for the column. + header?: string; + /** + * Text to display when hovering over the header text. This can be useful for + * providing additional information about the column when you want to keep the + * header text relatively short to manage the column width. + */ + headerDescription?: string; + /** + * Text to display when hovering over a cell. This can be useful for + * providing additional information about the column when the content is + * ellipsized to fit in the space. + */ + cellDescription?: string; + // Alignment of the content in the cell. + align?: 'left' | 'center' | 'right'; + + // When `true`, the column will be sortable. + enableSorting?: boolean; + + // Default sort order for the column. + sort?: 'asc' | 'desc'; + + /** + * Width of the column when rendered in a table. It should be a number in pixels + * or "auto" to allow the table to automatically adjust the width to fill + * space. + */ + width?: number | 'auto'; + // When `true`, the column will not be displayed. + hide?: boolean; +} + +export interface ValueCondition { + kind: 'Value'; + spec: { + value: string; + }; +} + +export interface RangeCondition { + kind: 'Range'; + spec: { + min?: number; + max?: number; + }; +} + +export interface RegexCondition { + kind: 'Regex'; + spec: { + expr: string; + }; +} + +export interface MiscCondition { + kind: 'Misc'; + spec: { + value: 'empty' | 'null' | 'NaN' | 'true' | 'false'; + }; +} + +export type Condition = ValueCondition | RangeCondition | RegexCondition | MiscCondition; + +export interface CellSettings { + condition: Condition; + text?: string; + textColor?: `#${string}`; + backgroundColor?: `#${string}`; +} + +/** + * The schema for a Table panel. + */ +export interface TableDefinition extends Definition { + kind: 'Table'; +} + +/** + * The Options object type supported by the Table panel plugin. + */ +export interface TableOptions { + // Change row height. + density?: TableDensity; + // When true, the table will try to automatically adjust the width of columns to fit without overflowing. + // Only for column without custom width specified in columnSettings. + defaultColumnWidth?: 'auto' | number; + // Customize column display and order them by their index in the array. + columnSettings?: ColumnSettings[]; + // Customize cell display based on their value. + cellSettings?: CellSettings[]; + // Apply transforms to the data before rendering the table. + transforms?: Transform[]; +} + +/** + * Creates the initial/empty options for a Table panel. + */ +export function createInitialTableOptions(): TableOptions { + return { + density: 'standard', + }; +} diff --git a/Table/src/test/mock-query-results.ts b/Table/src/test/mock-query-results.ts new file mode 100644 index 0000000..72d2331 --- /dev/null +++ b/Table/src/test/mock-query-results.ts @@ -0,0 +1,276 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { TimeSeriesData } from '@perses-dev/core'; + +export const MOCK_TIME_SERIES_QUERY_RESULT_MULTIVALUE = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: { + timeRange: { + start: new Date(1666625490000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [ + [1666479357903, 0.27700745551584494], + [1666479382282, 0.27701284657366565], + ], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [ + [1666479357903, 0.08486496097624885], + [1666479382282, 0.08486496097624885], + ], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], + }, + dataUpdatedAt: 1666500979895, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TIME_SERIES_QUERY_RESULT_SINGLEVALUE = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: { + timeRange: { + start: new Date(1666625535000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [[1666479357903, 0.27700745551584494]], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [[1666479357903, 0.08486496097624885]], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], + }, + dataUpdatedAt: 1666500979895, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TIME_SERIES_DATA_MULTIVALUE: TimeSeriesData = { + timeRange: { + start: new Date(1666625490000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [ + [1666479357903, 0.27700745551584494], + [1666479382282, 0.27701284657366565], + ], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [ + [1666479357903, 0.08486496097624885], + [1666479382282, 0.08486496097624885], + ], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], +}; + +export const MOCK_TIME_SERIES_DATA_SINGLEVALUE: TimeSeriesData = { + timeRange: { + start: new Date(1666625535000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [[1666479357903, 0.27700745551584494]], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [[1666479357903, 0.08486496097624885]], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], +}; + +export const MOCK_NULL_QUERY_RESULT = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: { + timeRange: { + start: new Date(1677376110000), + end: new Date(1677376410000), + }, + stepMs: 15000, + series: [ + { + name: 'node_memory_Buffers_bytes{env="demo",instance="demo.do.prometheus.io:9100",job="node"}', + values: [ + [1677376110000, 40000000], + [1677376125000, 40000000], + [1677376140000, null], + [1677376155000, null], + [1677376170000, null], + [1677376185000, null], + [1677376200000, null], + [1677376215000, 40013824], + [1677376230000, 40038400], + [1677376245000, 40054784], + [1677376260000, 40071168], + [1677376275000, 40075264], + [1677376290000, 40091648], + [1677376305000, 40099840], + [1677376320000, 40120320], + [1677376335000, 40128512], + [1677376350000, 40153088], + [1677376365000, 40165376], + [1677376380000, 40177664], + [1677376395000, 40194048], + [1677376410000, 40198144], + ], + formattedName: 'node_memory_Buffers_bytes{env="demo",instance="demo.do.prometheus.io:9100",job="node"}', + labels: { + env: 'demo', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + }, + }, + ], + }, + dataUpdatedAt: 1677376410000, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; diff --git a/Table/tsconfig.json b/Table/tsconfig.json new file mode 100644 index 0000000..da01ddb --- /dev/null +++ b/Table/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.base.json" +} \ No newline at end of file diff --git a/TimeSeriesChart/jest.config.ts b/TimeSeriesChart/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/TimeSeriesChart/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/TimeSeriesChart/package.json b/TimeSeriesChart/package.json index 960ef3d..c31b893 100644 --- a/TimeSeriesChart/package.json +++ b/TimeSeriesChart/package.json @@ -1,11 +1,13 @@ { "name": "@perses-dev/timeseries-chart", "private": true, - "version": "0.3.0", + "version": "0.4.0", "scripts": { "dev": "rsbuild dev", "build": "rsbuild build", - "lint": "eslint src --ext .ts,.tsx" + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" }, "dependencies": { "@module-federation/enhanced": "^0.1.11", @@ -24,7 +26,8 @@ "lodash": "^4.17.21", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0", - "use-resize-observer": "^9.0.0" + "use-resize-observer": "^9.0.0", + "immer": "^9.0.15" }, "files": [ "dist" diff --git a/TimeSeriesChart/rsbuild.config.ts b/TimeSeriesChart/rsbuild.config.ts index a3f2a67..15456e0 100644 --- a/TimeSeriesChart/rsbuild.config.ts +++ b/TimeSeriesChart/rsbuild.config.ts @@ -35,7 +35,7 @@ export default defineConfig({ new ModuleFederationPlugin({ name: 'TimeSeriesChart', exposes: { - './TimeSeriesChart': './src/TimeSeriesChart.tsx', + './TimeSeriesChart': './src/TimeSeriesChart.ts', }, shared: { react: { requiredVersion: '18.2.0', singleton: true }, diff --git a/TimeSeriesChart/src/QuerySettingsEditor.tsx b/TimeSeriesChart/src/QuerySettingsEditor.tsx new file mode 100644 index 0000000..113e7c8 --- /dev/null +++ b/TimeSeriesChart/src/QuerySettingsEditor.tsx @@ -0,0 +1,214 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { IconButton, MenuItem, Stack, TextField, Typography } from '@mui/material'; +import { InfoTooltip, OptionsEditorGroup, OptionsColorPicker } from '@perses-dev/components'; +import { ReactElement, RefObject, useEffect, useMemo, useRef } from 'react'; +import DeleteIcon from 'mdi-material-ui/DeleteOutline'; +import PlusIcon from 'mdi-material-ui/Plus'; +import produce from 'immer'; +import { useQueryCountContext } from '@perses-dev/plugin-system'; +import { QuerySettingsOptions } from './time-series-chart-model'; + +const DEFAULT_COLOR_MODE = 'fixed'; +const DEFAULT_COLOR_VALUE = '#555'; +const NO_INDEX_AVAILABLE = -1; // invalid array index value used to represent the fact that no query index is available + +export interface QuerySettingsEditorProps { + querySettingsList?: QuerySettingsOptions[]; + onChange: (querySettingsList: QuerySettingsOptions[]) => void; +} + +export function QuerySettingsEditor({ querySettingsList, onChange }: QuerySettingsEditorProps): ReactElement { + // Every time a new query settings input is added, we want to focus the recently added input + const recentlyAddedInputRef = useRef(null); + const focusRef = useRef(false); + useEffect(() => { + if (!recentlyAddedInputRef.current || !focusRef.current) return; + recentlyAddedInputRef.current?.focus(); + focusRef.current = false; + }, [querySettingsList?.length]); + + const handleQueryIndexChange = (e: React.ChangeEvent, i: number): void => { + if (querySettingsList !== undefined) { + onChange( + produce(querySettingsList, (draft) => { + const querySettings = draft?.[i]; + if (querySettings) { + querySettings.queryIndex = parseInt(e.target.value); + } + }) + ); + } + }; + + const handleColorModeChange = (e: React.ChangeEvent, i: number): void => { + if (querySettingsList !== undefined) { + onChange( + produce(querySettingsList, (draft) => { + if (draft !== undefined) { + const querySettings = draft[i]; + if (querySettings) { + querySettings.colorMode = e.target.value as QuerySettingsOptions['colorMode']; + } + } + }) + ); + } + }; + + const handleColorValueChange = (colorValue: string, i: number): void => { + if (querySettingsList !== undefined) { + onChange( + produce(querySettingsList, (draft) => { + if (draft !== undefined) { + const querySettings = draft[i]; + if (querySettings) { + querySettings.colorValue = colorValue; + } + } + }) + ); + } + }; + + const deleteQuerySettingsInput = (i: number): void => { + if (querySettingsList !== undefined) { + const updatedQuerySettingsList = produce(querySettingsList, (draft) => { + draft.splice(i, 1); + }); + onChange(updatedQuerySettingsList); + } + }; + + const queryCount = useQueryCountContext(); + + // Compute the list of query indexes for which query settings are not already defined. + // This is to avoid already-booked indexes to still be selectable in the dropdown(s) + const availableQueryIndexes = useMemo(() => { + const bookedQueryIndexes = querySettingsList?.map((querySettings) => querySettings.queryIndex) ?? []; + const allQueryIndexes = Array.from({ length: queryCount }, (_, i) => i); + return allQueryIndexes.filter((_, queryIndex) => !bookedQueryIndexes.includes(queryIndex)); + }, [querySettingsList, queryCount]); + + const firstAvailableQueryIndex = useMemo(() => { + return availableQueryIndexes[0] ?? NO_INDEX_AVAILABLE; + }, [availableQueryIndexes]); + + const defaultQuerySettings: QuerySettingsOptions = { + queryIndex: firstAvailableQueryIndex, + colorMode: DEFAULT_COLOR_MODE, + colorValue: DEFAULT_COLOR_VALUE, + }; + + const addQuerySettingsInput = (): void => { + focusRef.current = true; + if (querySettingsList === undefined) { + onChange([defaultQuerySettings]); + } else { + onChange( + produce(querySettingsList, (draft) => { + draft.push(defaultQuerySettings); + }) + ); + } + }; + + return ( + + + + + + ) : null + } + > + {querySettingsList && querySettingsList.length > 0 ? ( + querySettingsList.map((querySettings, i) => ( + { + handleQueryIndexChange(e, i); + }} + onColorModeChange={(e) => handleColorModeChange(e, i)} + onColorValueChange={(color) => handleColorValueChange(color, i)} + onDelete={() => { + deleteQuerySettingsInput(i); + }} + /> + )) + ) : ( + + No query settings defined + + )} + + ); +} + +export interface QuerySettingsInputProps { + querySettings: QuerySettingsOptions; + availableQueryIndexes: number[]; + onQueryIndexChange: (e: React.ChangeEvent) => void; + onColorModeChange: (e: React.ChangeEvent) => void; + onColorValueChange: (colorValue: string) => void; + onDelete: () => void; + inputRef?: RefObject; +} + +export function QuerySettingsInput({ + querySettings: { queryIndex, colorMode, colorValue }, + availableQueryIndexes, + onQueryIndexChange, + onColorModeChange, + onColorValueChange, + onDelete, + inputRef, +}: QuerySettingsInputProps): ReactElement { + // current query index should also be selectable + const selectableQueryIndexes = availableQueryIndexes.concat(queryIndex).sort((a, b) => a - b); + + return ( + + + {selectableQueryIndexes.map((queryIndex) => ( + + #{queryIndex + 1} + + ))} + + + Fixed (single) + Fixed + + + + + + + ); +} diff --git a/TimeSeriesChart/src/TimeChart.tsx b/TimeSeriesChart/src/TimeChart.tsx deleted file mode 100644 index e61fe77..0000000 --- a/TimeSeriesChart/src/TimeChart.tsx +++ /dev/null @@ -1,458 +0,0 @@ -// Copyright 2024 The Perses Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { - ChartInstance, - ChartInstanceFocusOpts, - CursorCoordinates, - DEFAULT_PINNED_CROSSHAIR, - DEFAULT_TOOLTIP_CONFIG, - EChart, - OnEventsType, - TimeChartSeriesMapping, - TimeChartTooltip, - TooltipConfig, - ZoomEventData, - clearHighlightedSeries, - enableDataZoom, - getClosestTimestamp, - getFormattedAxis, - getFormattedAxisLabel, - getPointInGrid, - restoreChart, - useChartsContext, - useTimeZone, -} from '@perses-dev/components'; -import { FormatOptions, TimeScale, TimeSeries, getCommonTimeScale } from '@perses-dev/core'; -import { utcToZonedTime } from 'date-fns-tz'; -import type { - EChartsCoreOption, - GridComponentOption, - LineSeriesOption, - TooltipComponentOption, - YAXisComponentOption, -} from 'echarts'; -import { BarChart as EChartsBarChart, LineChart as EChartsLineChart } from 'echarts/charts'; -import { - DataZoomComponent, - DatasetComponent, - GridComponent, - MarkAreaComponent, - MarkLineComponent, - MarkPointComponent, - TitleComponent, - ToolboxComponent, - TooltipComponent, -} from 'echarts/components'; -import { ECharts as EChartsInstance, use } from 'echarts/core'; -import { CanvasRenderer } from 'echarts/renderers'; -import { DatasetOption } from 'echarts/types/dist/shared.js'; -import isEqual from 'lodash/isEqual'; -import merge from 'lodash/merge'; -import { MouseEvent, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; - -use([ - EChartsLineChart, - EChartsBarChart, - GridComponent, - DatasetComponent, - DataZoomComponent, - MarkAreaComponent, - MarkLineComponent, - MarkPointComponent, - TitleComponent, - ToolboxComponent, - TooltipComponent, - CanvasRenderer, -]); - -export interface TimeChartProps { - height: number; - data: TimeSeries[]; - seriesMapping: TimeChartSeriesMapping; - timeScale?: TimeScale; - yAxis?: YAXisComponentOption; - format?: FormatOptions; - grid?: GridComponentOption; - tooltipConfig?: TooltipConfig; - noDataVariant?: 'chart' | 'message'; - syncGroup?: string; - isStackedBar?: boolean; - onDataZoom?: (e: ZoomEventData) => void; - onDoubleClick?: (e: MouseEvent) => void; - __experimentalEChartsOptionsOverride?: (options: EChartsCoreOption) => EChartsCoreOption; -} - -export const TimeChart = forwardRef(function TimeChart( - { - height, - data, - seriesMapping, - timeScale: timeScaleProp, - yAxis, - format, - grid, - isStackedBar = false, - tooltipConfig = DEFAULT_TOOLTIP_CONFIG, - noDataVariant = 'message', - syncGroup, - onDataZoom, - onDoubleClick, - __experimentalEChartsOptionsOverride, - }, - ref -) { - const { chartsTheme, enablePinning, lastTooltipPinnedCoords, setLastTooltipPinnedCoords } = useChartsContext(); - const isPinningEnabled = tooltipConfig.enablePinning && enablePinning; - const chartRef = useRef(); - const [showTooltip, setShowTooltip] = useState(true); - const [tooltipPinnedCoords, setTooltipPinnedCoords] = useState(null); - const [pinnedCrosshair, setPinnedCrosshair] = useState(null); - const [isDragging, setIsDragging] = useState(false); - const [startX, setStartX] = useState(0); - const { timeZone } = useTimeZone(); - let timeScale: TimeScale; - if (timeScaleProp === undefined) { - const commonTimeScale = getCommonTimeScale(data); - if (commonTimeScale === undefined) { - // set default to past 5 years - const today = new Date(); - const pastDate = new Date(today); - pastDate.setFullYear(today.getFullYear() - 5); - const todayMs = today.getTime(); - const pastDateMs = pastDate.getTime(); - timeScale = { startMs: pastDateMs, endMs: todayMs, stepMs: 1, rangeMs: todayMs - pastDateMs }; - } else { - timeScale = commonTimeScale; - } - } else { - timeScale = timeScaleProp; - } - - useImperativeHandle(ref, () => { - return { - highlightSeries({ name }: ChartInstanceFocusOpts) { - if (!chartRef.current) { - // when chart undef, do not highlight series when hovering over legend - return; - } - - chartRef.current.dispatchAction({ type: 'highlight', seriesId: name }); - }, - clearHighlightedSeries: () => { - if (!chartRef.current) { - // when chart undef, do not clear highlight series - return; - } - clearHighlightedSeries(chartRef.current); - }, - }; - }, []); - - const handleEvents: OnEventsType = useMemo(() => { - return { - datazoom: (params) => { - if (onDataZoom === undefined) { - setTimeout(() => { - // workaround so unpin happens after click event - setTooltipPinnedCoords(null); - }, 10); - } - if (onDataZoom === undefined || params.batch[0] === undefined) return; - const xAxisStartValue = params.batch[0].startValue; - const xAxisEndValue = params.batch[0].endValue; - if (xAxisStartValue !== undefined && xAxisEndValue !== undefined) { - const zoomEvent: ZoomEventData = { - start: xAxisStartValue, - end: xAxisEndValue, - }; - onDataZoom(zoomEvent); - } - }, - finished: () => { - if (chartRef.current !== undefined) { - enableDataZoom(chartRef.current); - } - }, - }; - }, [onDataZoom, setTooltipPinnedCoords]); - - const { noDataOption } = chartsTheme; - - const option: EChartsCoreOption = useMemo(() => { - // The "chart" `noDataVariant` is only used when the `timeSeries` is an - // empty array because a `null` value will throw an error. - if (data === null || (data.length === 0 && noDataVariant === 'message')) return noDataOption; - - // Utilizes ECharts dataset so raw data is separate from series option style properties - // https://apache.github.io/echarts-handbook/en/concepts/dataset/ - const dataset: DatasetOption[] = []; - const isLocalTimeZone = timeZone === 'local'; - data.map((d, index) => { - const values = d.values.map(([timestamp, value]) => { - const val: string | number = value === null ? '-' : value; // echarts use '-' to represent null data - return [isLocalTimeZone ? timestamp : utcToZonedTime(timestamp, timeZone), val]; - }); - dataset.push({ id: index, source: [...values], dimensions: ['time', 'value'] }); - }); - - const updatedSeriesMapping = - enablePinning && pinnedCrosshair !== null ? [...seriesMapping, pinnedCrosshair] : seriesMapping; - - const option: EChartsCoreOption = { - dataset: dataset, - series: updatedSeriesMapping, - xAxis: { - type: 'time', - min: isLocalTimeZone ? timeScale.startMs : utcToZonedTime(timeScale.startMs, timeZone), - max: isLocalTimeZone ? timeScale.endMs : utcToZonedTime(timeScale.endMs, timeZone), - axisLabel: { - hideOverlap: true, - formatter: getFormattedAxisLabel(timeScale.rangeMs ?? 0), - }, - axisPointer: { - snap: false, // important so shared crosshair does not lag - }, - }, - yAxis: getFormattedAxis(yAxis, format), - animation: false, - tooltip: { - show: true, - // ECharts tooltip content hidden by default since we use custom tooltip instead. - // Stacked bar uses ECharts tooltip so subgroup data shows correctly. - showContent: isStackedBar, - trigger: isStackedBar ? 'item' : 'axis', - appendToBody: true, - }, - // https://echarts.apache.org/en/option.html#axisPointer - axisPointer: { - type: 'line', - z: 0, // ensure point symbol shows on top of dashed line - triggerEmphasis: false, // https://github.com/apache/echarts/issues/18495 - triggerTooltip: false, - snap: false, // xAxis.axisPointer.snap takes priority - }, - toolbox: { - feature: { - dataZoom: { - icon: null, // https://stackoverflow.com/a/67684076/17575201 - yAxisIndex: 'none', - }, - }, - }, - grid, - }; - - if (__experimentalEChartsOptionsOverride) { - return __experimentalEChartsOptionsOverride(option); - } - - return option; - }, [ - data, - seriesMapping, - timeScale, - yAxis, - format, - grid, - noDataOption, - __experimentalEChartsOptionsOverride, - noDataVariant, - timeZone, - isStackedBar, - enablePinning, - pinnedCrosshair, - ]); - - // Update adjacent charts so tooltip is unpinned when current chart is clicked. - useEffect(() => { - // Only allow pinning one tooltip at a time, subsequent tooltip click unpins previous. - // Multiple tooltips can only be pinned if Ctrl or Cmd key is pressed while clicking. - const multipleTooltipsPinned = tooltipPinnedCoords !== null && lastTooltipPinnedCoords !== null; - if (multipleTooltipsPinned) { - if (!isEqual(lastTooltipPinnedCoords, tooltipPinnedCoords)) { - setTooltipPinnedCoords(null); - if (tooltipPinnedCoords !== null && pinnedCrosshair !== null) { - setPinnedCrosshair(null); - } - } - } - // tooltipPinnedCoords CANNOT be in dep array or tooltip pinning breaks in the current chart's onClick - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [lastTooltipPinnedCoords, seriesMapping]); - - return ( -
    { - // // TODO: confirm tooltip pinning works correctly on Windows, should e.preventDefault() be added here - // e.preventDefault(); // Prevent the default behaviour when right clicked - // }} - onClick={(e) => { - // Allows user to opt-in to multi tooltip pinning when Ctrl or Cmd key held down - const isControlKeyPressed = e.ctrlKey || e.metaKey; - if (isControlKeyPressed) { - e.preventDefault(); - } - - // Determine where on chart canvas to plot pinned crosshair as markLine. - const pointInGrid = getPointInGrid(e.nativeEvent.offsetX, e.nativeEvent.offsetY, chartRef.current); - if (pointInGrid === null) { - return; - } - - // Pin and unpin when clicking on chart canvas but not tooltip text. - if (isPinningEnabled && e.target instanceof HTMLCanvasElement) { - // Pin tooltip and update shared charts context to remember these coordinates. - const pinnedPos: CursorCoordinates = { - page: { - x: e.pageX, - y: e.pageY, - }, - client: { - x: e.clientX, - y: e.clientY, - }, - plotCanvas: { - x: e.nativeEvent.offsetX, - y: e.nativeEvent.offsetY, - }, - target: e.target, - }; - - setTooltipPinnedCoords((current) => { - if (current === null) { - return pinnedPos; - } else { - setPinnedCrosshair(null); - return null; - } - }); - - setPinnedCrosshair((current) => { - // Only add pinned crosshair line series when there is not one already in seriesMapping. - if (current === null) { - const cursorX = pointInGrid[0]; - - // Only need to loop through first dataset source since getCommonTimeScale ensures xAxis timestamps are consistent - const firstTimeSeriesValues = data[0]?.values; - const closestTimestamp = getClosestTimestamp(firstTimeSeriesValues, cursorX); - - // Crosshair snaps to nearest timestamp since cursor may be slightly to left or right - const pinnedCrosshair = merge({}, DEFAULT_PINNED_CROSSHAIR, { - markLine: { - data: [ - { - xAxis: closestTimestamp, - }, - ], - }, - } as LineSeriesOption); - return pinnedCrosshair; - } else { - // Clear previously set pinned crosshair - return null; - } - }); - - if (!isControlKeyPressed) { - setLastTooltipPinnedCoords(pinnedPos); - } - } - }} - onMouseDown={(e) => { - const { clientX } = e; - setIsDragging(true); - setStartX(clientX); - }} - onMouseMove={(e) => { - // Allow clicking inside tooltip to copy labels. - if (!(e.target instanceof HTMLCanvasElement)) { - return; - } - const { clientX } = e; - if (isDragging) { - const deltaX = clientX - startX; - if (deltaX > 0) { - // Hide tooltip when user drags to zoom. - setShowTooltip(false); - } - } - }} - onMouseUp={() => { - setIsDragging(false); - setStartX(0); - setShowTooltip(true); - }} - onMouseLeave={() => { - if (tooltipPinnedCoords === null) { - setShowTooltip(false); - } - if (chartRef.current !== undefined) { - clearHighlightedSeries(chartRef.current); - } - }} - onMouseEnter={() => { - setShowTooltip(true); - if (chartRef.current !== undefined) { - enableDataZoom(chartRef.current); - } - }} - onDoubleClick={(e) => { - setTooltipPinnedCoords(null); - // either dispatch ECharts restore action to return to orig state or allow consumer to define behavior - if (onDoubleClick === undefined) { - if (chartRef.current !== undefined) { - restoreChart(chartRef.current); - } - } else { - onDoubleClick(e); - } - }} - > - {/* Allows overrides prop to hide custom tooltip and use the ECharts option.tooltip instead */} - {showTooltip === true && - (option.tooltip as TooltipComponentOption)?.showContent === false && - tooltipConfig.hidden !== true && ( - { - // Unpins tooltip when clicking Pin icon in TooltipHeader. - setTooltipPinnedCoords(null); - // Clear previously set pinned crosshair. - setPinnedCrosshair(null); - }} - /> - )} - -
    - ); -}); diff --git a/TimeSeriesChart/src/TimeSeriesChart.ts b/TimeSeriesChart/src/TimeSeriesChart.ts new file mode 100644 index 0000000..6c87cb1 --- /dev/null +++ b/TimeSeriesChart/src/TimeSeriesChart.ts @@ -0,0 +1,27 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin } from '@perses-dev/plugin-system'; +import { createInitialTimeSeriesChartOptions, TimeSeriesChartOptions } from './time-series-chart-model'; +import { TimeSeriesChartOptionsEditorSettings } from './TimeSeriesChartOptionsEditorSettings'; +import { TimeSeriesChartPanel } from './TimeSeriesChartPanel'; + +/** + * The core TimeSeriesChart panel plugin for Perses. + */ +export const TimeSeriesChart: PanelPlugin = { + PanelComponent: TimeSeriesChartPanel, + supportedQueryTypes: ['TimeSeriesQuery'], + panelOptionsEditorComponents: [{ label: 'Settings', content: TimeSeriesChartOptionsEditorSettings }], + createInitialOptions: createInitialTimeSeriesChartOptions, +}; diff --git a/TimeSeriesChart/src/TimeSeriesChartOptionsEditorSettings.tsx b/TimeSeriesChart/src/TimeSeriesChartOptionsEditorSettings.tsx new file mode 100644 index 0000000..a85b823 --- /dev/null +++ b/TimeSeriesChart/src/TimeSeriesChartOptionsEditorSettings.tsx @@ -0,0 +1,114 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Button } from '@mui/material'; +import { produce } from 'immer'; +import { + OptionsEditorGroup, + OptionsEditorGrid, + OptionsEditorColumn, + ThresholdsEditor, + ThresholdsEditorProps, +} from '@perses-dev/components'; +import { LegendOptionsEditor, LegendOptionsEditorProps } from '@perses-dev/plugin-system'; +import { ReactElement } from 'react'; +import { + TimeSeriesChartOptions, + DEFAULT_VISUAL, + DEFAULT_Y_AXIS, + TimeSeriesChartOptionsEditorProps, +} from './time-series-chart-model'; +import { VisualOptionsEditor, VisualOptionsEditorProps } from './VisualOptionsEditor'; +import { YAxisOptionsEditor, YAxisOptionsEditorProps } from './YAxisOptionsEditor'; +import { QuerySettingsEditor, QuerySettingsEditorProps } from './QuerySettingsEditor'; + +export function TimeSeriesChartOptionsEditorSettings(props: TimeSeriesChartOptionsEditorProps): ReactElement { + const { onChange, value } = props; + + const handleLegendChange: LegendOptionsEditorProps['onChange'] = (newLegend) => { + // TODO (sjcobb): fix type, add position, fix glitch + onChange( + produce(value, (draft: TimeSeriesChartOptions) => { + draft.legend = newLegend; + }) + ); + }; + + const handleVisualChange: VisualOptionsEditorProps['onChange'] = (newVisual) => { + onChange( + produce(value, (draft: TimeSeriesChartOptions) => { + draft.visual = newVisual; + }) + ); + }; + + const handleYAxisChange: YAxisOptionsEditorProps['onChange'] = (newYAxis) => { + onChange( + produce(value, (draft: TimeSeriesChartOptions) => { + draft.yAxis = newYAxis; + }) + ); + }; + + const handleQuerySettingsChange: QuerySettingsEditorProps['onChange'] = (newQuerySettings) => { + onChange( + produce(value, (draft: TimeSeriesChartOptions) => { + draft.querySettings = newQuerySettings; + }) + ); + }; + + const handleThresholdsChange: ThresholdsEditorProps['onChange'] = (thresholds) => { + onChange( + produce(value, (draft: TimeSeriesChartOptions) => { + draft.thresholds = thresholds; + }) + ); + }; + + return ( + + + + + + + + + + + + + + + + + ); +} diff --git a/TimeSeriesChart/src/TimeSeriesChartPanel.test.tsx b/TimeSeriesChart/src/TimeSeriesChartPanel.test.tsx new file mode 100644 index 0000000..77adf45 --- /dev/null +++ b/TimeSeriesChart/src/TimeSeriesChartPanel.test.tsx @@ -0,0 +1,215 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { screen, render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { ChartsProvider, testChartsTheme } from '@perses-dev/components'; +import { TimeRangeValue, toAbsoluteTimeRange, UnknownSpec } from '@perses-dev/core'; +import { + PluginRegistry, + useDataQueries, + TimeRangeContext, + TimeSeriesQueryPlugin, + mockPluginRegistry, + MockPlugin, +} from '@perses-dev/plugin-system'; +import { VirtuosoMockContext } from 'react-virtuoso'; +import { MOCK_TIME_SERIES_QUERY_RESULT_MULTIVALUE, MOCK_TIME_SERIES_DATA_MULTIVALUE } from './test/mock-query-results'; +import { TimeSeriesChartPanel, TimeSeriesChartProps } from './TimeSeriesChartPanel'; + +jest.mock('@perses-dev/plugin-system', () => { + return { + ...jest.requireActual('@perses-dev/plugin-system'), + useDataQueries: jest.fn(), + }; +}); + +const FakeTimeSeriesQuery: TimeSeriesQueryPlugin = { + getTimeSeriesData: async () => { + return MOCK_TIME_SERIES_DATA_MULTIVALUE; + }, + OptionsEditorComponent: () => { + return
    Edit options here
    ; + }, + createInitialOptions: () => ({}), +}; + +const MOCK_PROM_QUERY_PLUGIN: MockPlugin = { + pluginType: 'TimeSeriesQuery', + kind: 'PrometheusTimeSeriesQuery', + plugin: FakeTimeSeriesQuery, +}; + +const TEST_TIME_RANGE: TimeRangeValue = { pastDuration: '1h' }; + +const TEST_TIME_SERIES_PANEL: TimeSeriesChartProps = { + contentDimensions: { + width: 500, + height: 500, + }, + spec: { + legend: { + position: 'right', + }, + yAxis: { + format: { unit: 'decimal', decimalPlaces: 2 }, + }, + }, +}; + +function getLegendByName(name?: string): HTMLElement { + if (typeof name !== 'string') { + throw new Error('Legend name must be a string.'); + } + + return screen.getByRole('listitem', { + name: (content, element) => { + return element.innerHTML.includes(name); + }, + }); +} + +describe('TimeSeriesChartPanel', () => { + beforeEach(() => { + // TODO: remove and instead use addMockPlugin after rest of runtime dependencies are mocked + (useDataQueries as jest.Mock).mockReturnValue({ + queryResults: MOCK_TIME_SERIES_QUERY_RESULT_MULTIVALUE, + isLoading: false, + isFetching: false, + }); + }); + + // Helper to render the panel with some context set + const renderPanel = (): void => { + const mockTimeRangeContext = { + refreshIntervalInMs: 0, + setRefreshInterval: (): Record => ({}), + timeRange: TEST_TIME_RANGE, + setTimeRange: (): Record => ({}), + absoluteTimeRange: toAbsoluteTimeRange(TEST_TIME_RANGE), + refresh: jest.fn(), + refreshKey: `${TEST_TIME_RANGE.pastDuration}:0`, + }; + + render( + + + + + + + + + + ); + }; + + it('should render the legend with unformatted series labels', async () => { + renderPanel(); + expect( + await screen.findByText( + 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"' + ) + ).toBeInTheDocument(); + }); + + it('should toggle selected state when a legend item is clicked', async () => { + renderPanel(); + + const seriesArr = MOCK_TIME_SERIES_DATA_MULTIVALUE.series; + const firstName = seriesArr[0]?.name; + const secondName = seriesArr[1]?.name; + + userEvent.click(getLegendByName(firstName)); + expect(getLegendByName(firstName)).toHaveClass('Mui-selected'); + expect(getLegendByName(secondName)).not.toHaveClass('Mui-selected'); + + userEvent.click(getLegendByName(secondName)); + expect(getLegendByName(firstName)).not.toHaveClass('Mui-selected'); + expect(getLegendByName(secondName)).toHaveClass('Mui-selected'); + }); + + it('should modify selected state when a legend item is clicked with shift key', async () => { + renderPanel(); + const seriesArr = MOCK_TIME_SERIES_DATA_MULTIVALUE.series; + + const firstName = seriesArr[0]?.name; + const secondName = seriesArr[1]?.name; + + // Add first legend item + userEvent.click(getLegendByName(firstName), { + shiftKey: true, + }); + expect(getLegendByName(firstName)).toHaveClass('Mui-selected'); + expect(getLegendByName(secondName)).not.toHaveClass('Mui-selected'); + + // Add second legend item + userEvent.click(getLegendByName(secondName), { + shiftKey: true, + }); + expect(getLegendByName(firstName)).toHaveClass('Mui-selected'); + expect(getLegendByName(secondName)).toHaveClass('Mui-selected'); + + // Remove first legend item + userEvent.click(getLegendByName(firstName), { + shiftKey: true, + }); + expect(getLegendByName(firstName)).not.toHaveClass('Mui-selected'); + expect(getLegendByName(secondName)).toHaveClass('Mui-selected'); + + // Remove second legend item + userEvent.click(getLegendByName(secondName), { + shiftKey: true, + }); + expect(getLegendByName(firstName)).not.toHaveClass('Mui-selected'); + expect(getLegendByName(secondName)).not.toHaveClass('Mui-selected'); + }); + + it('should modify selected state when a legend item is clicked with meta key', async () => { + renderPanel(); + const seriesArr = MOCK_TIME_SERIES_DATA_MULTIVALUE.series; + + // Falling back to a bogus string if not set to appease typescript. + const firstName = seriesArr[0]?.name; + const secondName = seriesArr[1]?.name; + + // Add first legend item + userEvent.click(getLegendByName(firstName), { + metaKey: true, + }); + + expect(getLegendByName(firstName)).toHaveClass('Mui-selected'); + expect(getLegendByName(secondName)).not.toHaveClass('Mui-selected'); + + // Add second legend item + userEvent.click(getLegendByName(secondName), { + metaKey: true, + }); + expect(getLegendByName(firstName)).toHaveClass('Mui-selected'); + expect(getLegendByName(secondName)).toHaveClass('Mui-selected'); + + // Remove first legend item + userEvent.click(getLegendByName(firstName), { + metaKey: true, + }); + expect(getLegendByName(firstName)).not.toHaveClass('Mui-selected'); + expect(getLegendByName(secondName)).toHaveClass('Mui-selected'); + + // Remove second legend item + userEvent.click(getLegendByName(secondName), { + metaKey: true, + }); + expect(getLegendByName(firstName)).not.toHaveClass('Mui-selected'); + expect(getLegendByName(secondName)).not.toHaveClass('Mui-selected'); + }); +}); diff --git a/TimeSeriesChart/src/TimeSeriesChart.tsx b/TimeSeriesChart/src/TimeSeriesChartPanel.tsx similarity index 80% rename from TimeSeriesChart/src/TimeSeriesChart.tsx rename to TimeSeriesChart/src/TimeSeriesChartPanel.tsx index 8d2ba54..42514fc 100644 --- a/TimeSeriesChart/src/TimeSeriesChart.tsx +++ b/TimeSeriesChart/src/TimeSeriesChartPanel.tsx @@ -1,4 +1,4 @@ -// Copyright 2024 The Perses Authors +// Copyright 2023 The Perses Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -11,71 +11,78 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* eslint-disable @typescript-eslint/ban-ts-comment */ -import { - ChartInstance, - ContentWithLegend, - DEFAULT_TOOLTIP_CONFIG, - LegendItem, - LegendProps, - SelectedLegendItemState, - TableColumnConfig, - TimeChartSeriesMapping, - TooltipConfig, - YAxisLabel, - ZoomEventData, - useChartsTheme, - useId, -} from '@perses-dev/components'; +import { ReactElement, useMemo, useRef, useState } from 'react'; +import { Box, useTheme } from '@mui/material'; +import type { GridComponentOption } from 'echarts'; +import merge from 'lodash/merge'; import { + getTimeSeriesValues, DEFAULT_LEGEND, - PanelDefinition, + getCalculations, + formatValue, StepOptions, TimeSeries, TimeSeriesValueTuple, - formatValue, - getCalculations, - getTimeSeriesValues, - useDeepMemo, } from '@perses-dev/core'; import { LEGEND_VALUE_CONFIG, - legendValues, + PanelProps, useDataQueries, useTimeRange, validateLegendSpec, + legendValues, } from '@perses-dev/plugin-system'; -import { GridComponentOption } from 'echarts'; -import merge from 'lodash/merge'; -import { useMemo, useRef, useState } from 'react'; -import { DEFAULT_FORMAT, DEFAULT_VISUAL, THRESHOLD_PLOT_INTERVAL, TimeSeriesChartOptions } from './model'; import { - convertPanelYAxis, - convertPercentThreshold, + ChartInstance, + YAxisLabel, + ZoomEventData, + useChartsTheme, + SelectedLegendItemState, + ContentWithLegend, + TableColumnConfig, + LegendItem, + LegendProps, + useId, + TimeChart, + TimeChartSeriesMapping, + TooltipConfig, + DEFAULT_TOOLTIP_CONFIG, + LoadingOverlay, +} from '@perses-dev/components'; +import { + TimeSeriesChartOptions, + DEFAULT_FORMAT, + DEFAULT_VISUAL, + THRESHOLD_PLOT_INTERVAL, + QuerySettingsOptions, +} from './time-series-chart-model'; +import { + getTimeSeries, getCommonTimeScaleForQueries, + convertPanelYAxis, getThresholdSeries, - getTimeSeries, + convertPercentThreshold, } from './utils/data-transform'; import { getSeriesColor } from './utils/palette-gen'; -import { TimeChart } from './TimeChart'; -interface ChartProps { - definition: PanelDefinition; - contentDimensions?: { width: number; height: number }; -} +export type TimeSeriesChartProps = PanelProps; + +// Using an "ALL" value to handle the case on first loading the chart where we +// want to select all, but do not want all of the legend items to be visually highlighted. +// This helps us differentiate those cases more clearly instead of inferring it +// based on the state of the data. This also helps us avoid some coding +// complexity around initializing a full record for the initial load that would +// currently require significantly more refactoring of this component. +// TODO: simplify this if we switch the list-based legend UI to use checkboxes, +// where we *would* want to visually select all items in this case. -export function TimeSeriesChart(props: ChartProps) { +export function TimeSeriesChartPanel(props: TimeSeriesChartProps): ReactElement | null { const { - definition: { - spec: { - plugin: { - spec: { thresholds, yAxis, tooltip, legend: legenFromProps, visual: visualFromProps }, - }, - }, - }, + spec: { thresholds, yAxis, tooltip, querySettings: querySettingsList }, contentDimensions, } = props; const chartsTheme = useChartsTheme(); + const muiTheme = useTheme(); const chartId = useId('time-series-panel'); const chartRef = useRef(null); @@ -102,17 +109,25 @@ export function TimeSeriesChart(props: ChartProps) { : undefined; // populate default 'position' and other future properties - const legend = - legenFromProps && validateLegendSpec(legenFromProps) ? merge({}, DEFAULT_LEGEND, legenFromProps) : undefined; + const legend = useMemo(() => { + return props.spec.legend && validateLegendSpec(props.spec.legend) + ? merge({}, DEFAULT_LEGEND, props.spec.legend) + : undefined; + }, [props.spec.legend]); // TODO: add support for y_axis_alt.format - const format = yAxis?.format ?? DEFAULT_FORMAT; + const format = props.spec.yAxis?.format ?? DEFAULT_FORMAT; // ensures there are fallbacks for unset properties since most // users should not need to customize visual display - const visual = merge({}, DEFAULT_VISUAL, visualFromProps); + const visual = useMemo(() => { + return merge({}, DEFAULT_VISUAL, props.spec.visual); + }, [props.spec.visual]); + // convert Perses dashboard format to be ECharts compatible - const echartsYAxis = convertPanelYAxis(yAxis); + const echartsYAxis = useMemo(() => { + return convertPanelYAxis(yAxis); + }, [yAxis]); const [selectedLegendItems, setSelectedLegendItems] = useState('ALL'); const [legendSorting, setLegendSorting] = useState['sorting']>(); @@ -120,7 +135,7 @@ export function TimeSeriesChart(props: ChartProps) { const { setTimeRange } = useTimeRange(); // Populate series data based on query results - const { timeScale, timeChartData, timeSeriesMapping, legendItems } = useDeepMemo(() => { + const { timeScale, timeChartData, timeSeriesMapping, legendItems } = useMemo(() => { // If loading or fetching, we display a loading indicator. // We skip the expensive loops below until we are done loading or fetching. if (isLoading || isFetching) { @@ -148,18 +163,23 @@ export function TimeSeriesChart(props: ChartProps) { // Index is counted across multiple queries which ensures the categorical color palette does not reset for every query let seriesIndex = 0; - // Total series count across all queries is needed before mapping below to determine which color palette to use - // This calculation should not impact performance since total number of queries rarely exceeds ~5 - let totalSeries = 0; - for (let i = 0; i < queryResults.length; i++) { - totalSeries += queryResults[i]?.data?.series?.length ?? 0; - } - // Mapping of each set of query results to be ECharts option compatible // TODO: Look into performance optimizations and moving parts of mapping to the lower level chart - for (const result of queryResults) { + for (let queryIndex = 0; queryIndex < queryResults.length; queryIndex++) { + const result = queryResults[queryIndex]; // Skip queries that are still loading or don't have data - if (result.isLoading || result.isFetching || result.data === undefined) continue; + if (!result || result.isLoading || result.isFetching || result.data === undefined) continue; + + // Retrieve querySettings for this query, if exists. + // queries & querySettings indices do not necessarily match, so we have to check the tail value of the $ref attribute + let querySettings: QuerySettingsOptions | undefined; + for (const item of querySettingsList ?? []) { + if (item.queryIndex === queryIndex) { + querySettings = item; + // We don't break the loop here just in case there are multiple querySettings defined for the + // same queryIndex, because in that case we want the last one to take precedence. + } + } for (let i = 0; i < result.data.series.length; i++) { const timeSeries: TimeSeries | undefined = result.data.series[i]; @@ -175,9 +195,11 @@ export function TimeSeriesChart(props: ChartProps) { // ECharts type for color is not always an array but it is always an array in ChartsProvider categoricalPalette: categoricalPalette as string[], visual, + muiPrimaryColor: muiTheme.palette.primary.main, seriesName: formattedSeriesName, seriesIndex, - totalSeries, + querySettings: querySettings, + queryHasMultipleResults: (queryResults[queryIndex]?.data?.series?.length ?? 0) > 1, }); // We add a unique id for the chart to disambiguate items across charts @@ -267,7 +289,22 @@ export function TimeSeriesChart(props: ChartProps) { timeSeriesMapping, legendItems, }; - }, [queryResults, thresholds, selectedLegendItems, legend, visual, isFetching, isLoading, yAxis?.max, yAxis?.min]); + }, [ + queryResults, + thresholds, + selectedLegendItems, + legend, + visual, + querySettingsList, + isFetching, + isLoading, + yAxis?.max, + yAxis?.min, + categoricalPalette, + chartId, + chartsTheme.thresholds, + muiTheme.palette.primary.main, + ]); // Translate the legend values into columns for the table legend. const legendColumns = useMemo(() => { @@ -311,25 +348,7 @@ export function TimeSeriesChart(props: ChartProps) { } if (isLoading || isFetching) { - return ( -
    - {/* */} -
    Loading...
    -
    - ); + return ; } // At this point, we have a response from the backend for all queries. (We're past loading === true). @@ -351,7 +370,7 @@ export function TimeSeriesChart(props: ChartProps) { bottom: 0, }; - const handleDataZoom = (event: ZoomEventData) => { + const handleDataZoom = (event: ZoomEventData): void => { // TODO: add ECharts transition animation on zoom setTimeRange({ start: new Date(event.start), end: new Date(event.end) }); }; @@ -372,7 +391,7 @@ export function TimeSeriesChart(props: ChartProps) { }; return ( -
    + { + onItemMouseOver: (e, { id }): void => { chartRef.current?.highlightSeries({ name: id }); }, - onItemMouseOut: () => { + onItemMouseOut: (): void => { chartRef.current?.clearHighlightedSeries(); }, } @@ -402,7 +421,7 @@ export function TimeSeriesChart(props: ChartProps) { > {({ height, width }) => { return ( -
    + {yAxis && yAxis.show && yAxis.label && } 0 ? 'chart' : 'message'} /> -
    +
    ); }} -
    + ); } diff --git a/TimeSeriesChart/src/VisualOptionsEditor.test.tsx b/TimeSeriesChart/src/VisualOptionsEditor.test.tsx new file mode 100644 index 0000000..6eba89f --- /dev/null +++ b/TimeSeriesChart/src/VisualOptionsEditor.test.tsx @@ -0,0 +1,55 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { render, screen, fireEvent } from '@testing-library/react'; +import { TimeSeriesChartVisualOptions, VISUAL_CONFIG } from './time-series-chart-model'; +import { VisualOptionsEditor } from './VisualOptionsEditor'; + +describe('VisualOptionsEditor', () => { + const renderVisualOptionsEditor = (value: TimeSeriesChartVisualOptions, onChange = jest.fn()): void => { + render(); + }; + + const getLineWidthSlider = (): HTMLElement => { + return screen.getByTestId(VISUAL_CONFIG.lineWidth.testId); + }; + + it('can update the line width visual option', () => { + const onChange = jest.fn(); + renderVisualOptionsEditor({ lineWidth: 3, pointRadius: 2 }, onChange); + + expect(screen.getByText(VISUAL_CONFIG.lineWidth.label)).toBeInTheDocument(); + + const sliderInput = getLineWidthSlider(); + + // MUI Slider computes the return value based on span elements, mock initial position + sliderInput.getBoundingClientRect = jest.fn(() => { + return { + bottom: 200, + height: 30, + left: 20, + right: 500, + top: 250, + width: 550, + x: 20, + y: 250, + toJSON: (): Record => ({}), + }; + }); + expect(sliderInput).toBeInTheDocument(); + + // to move slider and update visual options + fireEvent.mouseDown(sliderInput, { clientX: 220, clientY: 100 }); + expect(onChange).toHaveBeenCalledWith({ lineWidth: 1.25, pointRadius: 2.75 }); + }); +}); diff --git a/TimeSeriesChart/src/VisualOptionsEditor.tsx b/TimeSeriesChart/src/VisualOptionsEditor.tsx new file mode 100644 index 0000000..8c5160e --- /dev/null +++ b/TimeSeriesChart/src/VisualOptionsEditor.tsx @@ -0,0 +1,163 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { produce } from 'immer'; +import { Slider, Switch, ToggleButton, ToggleButtonGroup } from '@mui/material'; +import { OptionsEditorControl, OptionsEditorGroup, SettingsAutocomplete } from '@perses-dev/components'; +import { ReactElement } from 'react'; +import { + DEFAULT_AREA_OPACITY, + DEFAULT_CONNECT_NULLS, + DEFAULT_LINE_WIDTH, + DEFAULT_POINT_RADIUS, + POINT_SIZE_OFFSET, + STACK_CONFIG, + StackOptions, + STACK_OPTIONS, + VISUAL_CONFIG, + TimeSeriesChartVisualOptions, +} from './time-series-chart-model'; + +export interface VisualOptionsEditorProps { + value: TimeSeriesChartVisualOptions; + onChange: (visual: TimeSeriesChartVisualOptions) => void; +} + +export function VisualOptionsEditor({ value, onChange }: VisualOptionsEditorProps): ReactElement { + const handleLineWidthChange = (_: Event, sliderValue: number | number[]): void => { + const newValue = Array.isArray(sliderValue) ? sliderValue[0] : sliderValue; + const symbolSize = newValue !== undefined ? newValue + POINT_SIZE_OFFSET : DEFAULT_POINT_RADIUS; + onChange( + produce(value, (draft) => { + draft.lineWidth = newValue; + draft.pointRadius = symbolSize; + }) + ); + }; + + const handleAreaOpacityChange = (_: Event, sliderValue: number | number[]): void => { + const newValue = Array.isArray(sliderValue) ? sliderValue[0] : sliderValue; + onChange( + produce(value, (draft) => { + draft.areaOpacity = newValue; + }) + ); + }; + + const currentStack: StackOptions = value.stack ?? 'none'; + const stackConfig = STACK_CONFIG[currentStack]; + + return ( + + { + onChange({ + ...value, + display: newValue, + }); + }} + > + + Line + + + Bar + + + } + /> + + } + /> + + } + /> + { + const updatedValue: TimeSeriesChartVisualOptions = { + ...value, + stack: newValue.id === 'none' ? undefined : newValue.id, // stack is optional so remove property when 'None' is selected + }; + // stacked area chart preset to automatically set area under a curve shading + if (newValue.id === 'all' && !value.areaOpacity) { + updatedValue.areaOpacity = 0.3; + } + onChange(updatedValue); + }} + disabled={value === undefined} + disableClearable + > + } + /> + { + onChange({ + ...value, + connectNulls: e.target.checked, + }); + }} + /> + } + /> + + ); +} diff --git a/TimeSeriesChart/src/YAxisOptionsEditor.tsx b/TimeSeriesChart/src/YAxisOptionsEditor.tsx new file mode 100644 index 0000000..88c8b47 --- /dev/null +++ b/TimeSeriesChart/src/YAxisOptionsEditor.tsx @@ -0,0 +1,104 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Switch, TextField } from '@mui/material'; +import { OptionsEditorControl, OptionsEditorGroup, FormatControls } from '@perses-dev/components'; +import { ReactElement } from 'react'; +import { DEFAULT_FORMAT, DEFAULT_Y_AXIS, TimeSeriesChartYAxisOptions, Y_AXIS_CONFIG } from './time-series-chart-model'; + +export interface YAxisOptionsEditorProps { + value: TimeSeriesChartYAxisOptions; + onChange: (yAxis: TimeSeriesChartYAxisOptions) => void; +} + +export function YAxisOptionsEditor({ value, onChange }: YAxisOptionsEditorProps): ReactElement { + return ( + + { + onChange({ + ...value, + show: e.target.checked, + }); + }} + /> + } + /> + + onChange({ + ...value, + format: newFormat, + }) + } + /> + + onChange({ + ...value, + label: e.target.value, + }) + } + placeholder="Default" + /> + } + /> + { + // ensure empty value resets to undef to allow chart to calculate min + const newValue = e.target.value ? Number(e.target.value) : undefined; + onChange({ + ...value, + min: newValue, + }); + }} + placeholder="Default" + /> + } + /> + { + // ensure empty value resets to undef to allow chart to calculate max + const newValue = e.target.value ? Number(e.target.value) : undefined; + onChange({ + ...value, + max: newValue, + }); + }} + placeholder="Default" + /> + } + /> + + ); +} diff --git a/TimeSeriesChart/src/setup-tests.ts b/TimeSeriesChart/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/TimeSeriesChart/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/TimeSeriesChart/src/test/mock-query-results.ts b/TimeSeriesChart/src/test/mock-query-results.ts new file mode 100644 index 0000000..72d2331 --- /dev/null +++ b/TimeSeriesChart/src/test/mock-query-results.ts @@ -0,0 +1,276 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { TimeSeriesData } from '@perses-dev/core'; + +export const MOCK_TIME_SERIES_QUERY_RESULT_MULTIVALUE = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: { + timeRange: { + start: new Date(1666625490000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [ + [1666479357903, 0.27700745551584494], + [1666479382282, 0.27701284657366565], + ], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [ + [1666479357903, 0.08486496097624885], + [1666479382282, 0.08486496097624885], + ], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], + }, + dataUpdatedAt: 1666500979895, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TIME_SERIES_QUERY_RESULT_SINGLEVALUE = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: { + timeRange: { + start: new Date(1666625535000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [[1666479357903, 0.27700745551584494]], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [[1666479357903, 0.08486496097624885]], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], + }, + dataUpdatedAt: 1666500979895, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TIME_SERIES_DATA_MULTIVALUE: TimeSeriesData = { + timeRange: { + start: new Date(1666625490000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [ + [1666479357903, 0.27700745551584494], + [1666479382282, 0.27701284657366565], + ], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [ + [1666479357903, 0.08486496097624885], + [1666479382282, 0.08486496097624885], + ], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], +}; + +export const MOCK_TIME_SERIES_DATA_SINGLEVALUE: TimeSeriesData = { + timeRange: { + start: new Date(1666625535000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [[1666479357903, 0.27700745551584494]], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [[1666479357903, 0.08486496097624885]], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], +}; + +export const MOCK_NULL_QUERY_RESULT = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: { + timeRange: { + start: new Date(1677376110000), + end: new Date(1677376410000), + }, + stepMs: 15000, + series: [ + { + name: 'node_memory_Buffers_bytes{env="demo",instance="demo.do.prometheus.io:9100",job="node"}', + values: [ + [1677376110000, 40000000], + [1677376125000, 40000000], + [1677376140000, null], + [1677376155000, null], + [1677376170000, null], + [1677376185000, null], + [1677376200000, null], + [1677376215000, 40013824], + [1677376230000, 40038400], + [1677376245000, 40054784], + [1677376260000, 40071168], + [1677376275000, 40075264], + [1677376290000, 40091648], + [1677376305000, 40099840], + [1677376320000, 40120320], + [1677376335000, 40128512], + [1677376350000, 40153088], + [1677376365000, 40165376], + [1677376380000, 40177664], + [1677376395000, 40194048], + [1677376410000, 40198144], + ], + formattedName: 'node_memory_Buffers_bytes{env="demo",instance="demo.do.prometheus.io:9100",job="node"}', + labels: { + env: 'demo', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + }, + }, + ], + }, + dataUpdatedAt: 1677376410000, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; diff --git a/TimeSeriesChart/src/model.ts b/TimeSeriesChart/src/time-series-chart-model.ts similarity index 54% rename from TimeSeriesChart/src/model.ts rename to TimeSeriesChart/src/time-series-chart-model.ts index 6ba565a..daad2bd 100644 --- a/TimeSeriesChart/src/model.ts +++ b/TimeSeriesChart/src/time-series-chart-model.ts @@ -1,4 +1,4 @@ -// Copyright 2024 The Perses Authors +// Copyright 2023 The Perses Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -11,7 +11,35 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CalculationType, FormatOptions, LegendOptionsBase, ThresholdOptions } from '@perses-dev/core'; +import { Definition, ThresholdOptions, FormatOptions } from '@perses-dev/core'; +import { OptionsEditorProps, LegendSpecOptions } from '@perses-dev/plugin-system'; + +/** + * The schema for a TimeSeriesChart panel. + */ +export interface TimeSeriesChartDefinition extends Definition { + kind: 'TimeSeriesChart'; +} + +/** + * The Options object supported by the TimeSeriesChartPanel plugin. + */ +export interface TimeSeriesChartOptions { + legend?: LegendSpecOptions; + yAxis?: TimeSeriesChartYAxisOptions; + thresholds?: ThresholdOptions; + visual?: TimeSeriesChartVisualOptions; + tooltip?: TooltipSpecOptions; + querySettings?: QuerySettingsOptions[]; +} + +export interface QuerySettingsOptions { + queryIndex: number; + colorMode: 'fixed' | 'fixed-single'; + colorValue: string; +} + +export type TimeSeriesChartOptionsEditorProps = OptionsEditorProps; export interface TimeSeriesChartYAxisOptions { show?: boolean; @@ -25,21 +53,10 @@ export interface TooltipSpecOptions { enablePinning: boolean; } -export interface TimeSeriesChartOptions { - legend?: LegendSpecOptions; - yAxis?: TimeSeriesChartYAxisOptions; - thresholds?: ThresholdOptions; - visual?: TimeSeriesChartVisualOptions; - tooltip?: TooltipSpecOptions; -} - export interface TimeSeriesChartPaletteOptions { mode: 'auto' | 'categorical'; - // colors: string []; // TODO: add colors to override ECharts theme } -export type StackOptions = 'none' | 'all'; // TODO: add percent option support - export type TimeSeriesChartVisualOptions = { display?: 'line' | 'bar'; lineWidth?: number; @@ -51,31 +68,28 @@ export type TimeSeriesChartVisualOptions = { connectNulls?: boolean; }; -export const legendValues: CalculationType[] = [ - 'mean', - 'first', - 'first-number', - 'last', - 'last-number', - 'min', - 'max', - 'sum', -]; -export type LegendValue = (typeof legendValues)[number]; - -// Note: explicitly defining different options for the legend spec and -// legend component that extend from some common options, so we can allow the -// component and the spec to diverge in some upcoming work. -export interface LegendSpecOptions extends LegendOptionsBase { - values?: LegendValue[]; -} - export const DEFAULT_FORMAT: FormatOptions = { unit: 'decimal', shortValues: true, }; -export const DEFAULT_LINE_WIDTH = 1.5; +export const DEFAULT_Y_AXIS: TimeSeriesChartYAxisOptions = { + show: true, + label: '', + format: DEFAULT_FORMAT, + min: undefined, + max: undefined, +}; + +export const Y_AXIS_CONFIG = { + show: { label: 'Show' }, + label: { label: 'Label' }, + unit: { label: 'Unit' }, + min: { label: 'Min' }, + max: { label: 'Max' }, +}; + +export const DEFAULT_LINE_WIDTH = 1.25; export const DEFAULT_AREA_OPACITY = 0; // How much larger datapoint symbols are than line width, also applied in VisualOptionsEditor. @@ -91,18 +105,66 @@ export const DEFAULT_VISUAL: TimeSeriesChartVisualOptions = { connectNulls: DEFAULT_CONNECT_NULLS, }; +// Controls how often static threshold values should be plotted so threshold data shows +// in tooltip without flicker. export const THRESHOLD_PLOT_INTERVAL = 15; +export const VISUAL_CONFIG = { + lineWidth: { + label: 'Line Width', + testId: 'slider-line-width', + min: 0.25, + max: 3, + step: 0.25, + }, + pointRadius: { + label: 'Point Radius', + testId: 'slider-point-radius', + min: 0, + max: 6, + step: 0.25, + }, + areaOpacity: { + label: 'Area Opacity', + testId: 'slider-area-opacity', + min: 0, + max: 1, + step: 0.05, + }, + stack: { + label: 'Stack Series', + }, + connectNulls: { + label: 'Connect Nulls', + }, +}; + +// None is equivalent to undefined since stack is optional +export type StackOptions = 'none' | 'all'; // TODO: add percent option support + +export const STACK_CONFIG = { + none: { label: 'None' }, + all: { label: 'All' }, + // TODO: enable option after 'Percent' implemented + // percent: { label: 'Percent', disabled: true }, // hidden since not implemented yet +}; + +export const STACK_OPTIONS = Object.entries(STACK_CONFIG).map(([id, config]) => { + return { + id: id as StackOptions, + ...config, + }; +}); + // Both of these constants help produce a value that is LESS THAN the initial value. // For positive values, we multiply by a number less than 1 to get this outcome. // For negative values, we multiply to a number greater than 1 to get this outcome. export const POSITIVE_MIN_VALUE_MULTIPLIER = 0.8; export const NEGATIVE_MIN_VALUE_MULTIPLIER = 1.2; -export const DEFAULT_Y_AXIS: TimeSeriesChartYAxisOptions = { - show: true, - label: '', - format: DEFAULT_FORMAT, - min: undefined, - max: undefined, -}; +/** + * Creates an initial/empty options object for the TimeSeriesChartPanel. + */ +export function createInitialTimeSeriesChartOptions(): TimeSeriesChartOptions { + return {}; +} diff --git a/TimeSeriesChart/src/utils/data-transform.test.ts b/TimeSeriesChart/src/utils/data-transform.test.ts new file mode 100644 index 0000000..995ef9b --- /dev/null +++ b/TimeSeriesChart/src/utils/data-transform.test.ts @@ -0,0 +1,117 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { LegacyTimeSeries } from '@perses-dev/components'; +import { TimeSeriesChartYAxisOptions } from '../time-series-chart-model'; +import { convertPercentThreshold, convertPanelYAxis, roundDown } from './data-transform'; + +const MAX_VALUE = 120; +const MOCK_ECHART_TIME_SERIES_DATA: LegacyTimeSeries[] = [ + { + data: [10, 30, 80, 50], + }, + { + data: [20, MAX_VALUE, 17, 30], + }, +]; + +describe('convertPercentThreshold', () => { + it('should return 25 if percent threshold is 25 and max is 100', () => { + const value = convertPercentThreshold(25, MOCK_ECHART_TIME_SERIES_DATA, 100); + expect(value).toEqual(25); + }); + + it('should return 60 if percent threshold is 50, max is 100, and min is 20', () => { + const value = convertPercentThreshold(50, MOCK_ECHART_TIME_SERIES_DATA, 100, 20); + expect(value).toEqual(60); + }); + + it('should return 50% of the max value in time series data if max is undefined', () => { + const value = convertPercentThreshold(50, MOCK_ECHART_TIME_SERIES_DATA); + expect(value).toEqual(0.5 * MAX_VALUE); + }); +}); + +describe('convertPanelYAxis', () => { + it('should convert a Perses yAxis spec to the ECharts equivalent', () => { + const persesAxis: TimeSeriesChartYAxisOptions = { + show: true, + label: 'Axis Label', + format: { + unit: 'percent-decimal', + decimalPlaces: 0, + }, + min: 0.1, + max: 1, + }; + const echartsAxis = convertPanelYAxis(persesAxis); + // Axis label is handled outside of echarts since it is built with a custom React component. + expect(echartsAxis).toEqual({ + show: true, + max: 1, + min: 0.1, + axisLabel: { + show: true, + }, + }); + }); +}); + +const ROUND_DOWN_TESTS = [ + { + value: -400305, + expected: -500000, + }, + { + value: -633, + expected: -700, + }, + { + value: -5.99, + expected: -6, + }, + { + value: -0.123, + expected: -0.2, + }, + { + value: -0.000543, + expected: -0.0006, + }, + { + value: 0.000543, + expected: 0.0005, + }, + { + value: 0.123, + expected: 0.1, + }, + { + value: 5.99, + expected: 5, + }, + { + value: 633, + expected: 600, + }, + { + value: 400305, + expected: 400000, + }, +]; + +describe('roundDown', () => { + it.each(ROUND_DOWN_TESTS)('returns $expected when the input is $value', ({ value, expected }) => { + expect(roundDown(value)).toEqual(expected); + }); +}); diff --git a/TimeSeriesChart/src/utils/data-transform.ts b/TimeSeriesChart/src/utils/data-transform.ts index 825c54d..1c3e025 100644 --- a/TimeSeriesChart/src/utils/data-transform.ts +++ b/TimeSeriesChart/src/utils/data-transform.ts @@ -1,4 +1,4 @@ -// Copyright 2024 The Perses Authors +// Copyright 2023 The Perses Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -39,7 +39,7 @@ import { NEGATIVE_MIN_VALUE_MULTIPLIER, TimeSeriesChartVisualOptions, TimeSeriesChartYAxisOptions, -} from '../model'; +} from '../time-series-chart-model'; export type RunningQueriesState = ReturnType; @@ -175,7 +175,7 @@ export function getTimeSeries( symbolSize: pointRadius, lineStyle: { width: lineWidth, - opacity: 0.8, + opacity: 0.95, }, areaStyle: { opacity: visual.areaOpacity ?? DEFAULT_AREA_OPACITY, @@ -185,7 +185,7 @@ export function getTimeSeries( focus: 'series', disabled: visual.areaOpacity !== undefined && visual.areaOpacity > 0, // prevents flicker when moving cursor between shaded regions lineStyle: { - width: lineWidth + 1.5, + width: lineWidth + 1, opacity: 1, }, }, @@ -249,7 +249,7 @@ export function convertPercentThreshold( data: LegacyTimeSeries[] | TimeSeries[], max?: number, min?: number -) { +): number { const percentDecimal = percent / 100; const adjustedMax = max ?? findMax(data); const adjustedMin = min ?? 0; @@ -257,7 +257,7 @@ export function convertPercentThreshold( return percentDecimal * total + adjustedMin; } -function findMax(data: LegacyTimeSeries[] | TimeSeries[]) { +function findMax(data: LegacyTimeSeries[] | TimeSeries[]): number { let max = 0; if (data.length && data[0] !== undefined && (data as TimeSeries[])[0]?.values) { (data as TimeSeries[]).forEach((series) => { @@ -288,7 +288,10 @@ function findMax(data: LegacyTimeSeries[] | TimeSeries[]) { */ export function convertPanelYAxis(inputAxis: TimeSeriesChartYAxisOptions = {}): YAXisComponentOption { const yAxis: YAXisComponentOption = { - show: inputAxis?.show ?? DEFAULT_Y_AXIS.show, + show: true, + axisLabel: { + show: inputAxis?.show ?? DEFAULT_Y_AXIS.show, + }, min: inputAxis?.min, max: inputAxis?.max, }; @@ -296,7 +299,7 @@ export function convertPanelYAxis(inputAxis: TimeSeriesChartYAxisOptions = {}): // Set the y-axis minimum relative to the data if (inputAxis?.min === undefined) { // https://echarts.apache.org/en/option.html#yAxis.min - yAxis.min = (value) => { + yAxis.min = (value): number => { if (value.min >= 0 && value.min <= 1) { // Helps with PercentDecimal units, or datasets that return 0 or 1 booleans return 0; @@ -323,7 +326,7 @@ export function convertPanelYAxis(inputAxis: TimeSeriesChartYAxisOptions = {}): * 2. 0.567 --> 0.5 * 3. -12 --> -20 */ -export function roundDown(num: number) { +export function roundDown(num: number): number { const magnitude = Math.floor(Math.log10(Math.abs(num))); const firstDigit = Math.floor(num / Math.pow(10, magnitude)); return firstDigit * Math.pow(10, magnitude); diff --git a/TimeSeriesChart/src/utils/palette-gen.test.ts b/TimeSeriesChart/src/utils/palette-gen.test.ts new file mode 100644 index 0000000..7427a32 --- /dev/null +++ b/TimeSeriesChart/src/utils/palette-gen.test.ts @@ -0,0 +1,235 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { TimeSeriesChartVisualOptions } from '../time-series-chart-model'; +import { getSeriesColor, getAutoPaletteColor, getCategoricalPaletteColor, SeriesColorProps } from './palette-gen'; + +describe('getSeriesColor', () => { + const fallbackColor = '#000'; + const testCategoricalPalette = [ + '#56B4E9', // lt blue + '#009E73', // med green + '#0072B2', // dk blue + '#CC79A7', // lt purple + '#F0E442', // yellow + '#E69F00', // orange + '#D55E00', // red + ]; + const testSeriesName = 'Test series name'; + const testSeriesNameGeneratedColor = 'hsla(27.48,50%,50%,0.9)'; + + it('should return the first color from default Categorical palette', () => { + const props: SeriesColorProps = { + categoricalPalette: testCategoricalPalette, + visual: { + palette: { + mode: 'categorical', + }, + }, + muiPrimaryColor: fallbackColor, + seriesName: testSeriesName, + seriesIndex: 0, + }; + const paletteColor = getSeriesColor(props); + expect(paletteColor).toEqual(testCategoricalPalette[0]); + }); + + it('should return the last color from default Categorical palette', () => { + const props: SeriesColorProps = { + categoricalPalette: testCategoricalPalette, + visual: { + palette: { + mode: 'categorical', + }, + }, + muiPrimaryColor: fallbackColor, + seriesName: testSeriesName, + seriesIndex: 6, + }; + const paletteColor = getSeriesColor(props); + expect(paletteColor).toEqual('#D55E00'); + }); + + it('should return color from the generative Auto palette when visual option is defined', () => { + const visualOptionAuto: TimeSeriesChartVisualOptions = { + palette: { + mode: 'auto', + }, + }; + const props: SeriesColorProps = { + categoricalPalette: testCategoricalPalette, + visual: visualOptionAuto, + muiPrimaryColor: fallbackColor, + seriesName: testSeriesName, + seriesIndex: 0, + }; + const paletteColor = getSeriesColor(props); + expect(paletteColor).toEqual(testSeriesNameGeneratedColor); + }); + + it('should return color from the Categorical palette when visual option is defined', () => { + const visualOptionCategorical: TimeSeriesChartVisualOptions = { + palette: { + mode: 'categorical', + }, + }; + const props: SeriesColorProps = { + categoricalPalette: testCategoricalPalette, + visual: visualOptionCategorical, + muiPrimaryColor: fallbackColor, + seriesName: testSeriesName, + seriesIndex: 0, + }; + const paletteColor = getSeriesColor(props); + expect(paletteColor).toEqual(testCategoricalPalette[0]); + }); + + it('should return Auto generated color when the Categorical palette is undefined', () => { + const props = { + categoricalPalette: undefined, + visual: {}, + muiPrimaryColor: fallbackColor, + seriesName: testSeriesName, + seriesIndex: 0, + } as unknown as SeriesColorProps; + const paletteColor = getSeriesColor(props); + expect(paletteColor).toEqual(testSeriesNameGeneratedColor); + }); + + it('should return color set in querySettings when colorMode=fixed & queryHasMultipleResults=true', () => { + const visualOptionSingleSeriesOverride: TimeSeriesChartVisualOptions = { + palette: { + mode: 'auto', + }, + }; + const props: SeriesColorProps = { + categoricalPalette: testCategoricalPalette, + visual: visualOptionSingleSeriesOverride, + muiPrimaryColor: fallbackColor, + seriesName: testSeriesName, + seriesIndex: 0, + querySettings: { + queryIndex: 0, + colorMode: 'fixed', + colorValue: '#000', + }, + queryHasMultipleResults: true, + }; + const paletteColor = getSeriesColor(props); + expect(paletteColor).toEqual('#000'); + }); + + it('should return color set in querySettings when colorMode=fixed & queryHasMultipleResults=false', () => { + const visualOptionSingleSeriesOverride: TimeSeriesChartVisualOptions = { + palette: { + mode: 'auto', + }, + }; + const props: SeriesColorProps = { + categoricalPalette: testCategoricalPalette, + visual: visualOptionSingleSeriesOverride, + muiPrimaryColor: fallbackColor, + seriesName: testSeriesName, + seriesIndex: 0, + querySettings: { + queryIndex: 0, + colorMode: 'fixed', + colorValue: '#000', + }, + queryHasMultipleResults: false, + }; + const paletteColor = getSeriesColor(props); + expect(paletteColor).toEqual('#000'); + }); + + it('should fallback to regular palette instead of using querySettings when colorMode=fixed-single & queryHasMultipleResults=true', () => { + const visualOptionSingleSeriesOverride: TimeSeriesChartVisualOptions = { + palette: { + mode: 'auto', + }, + }; + const props: SeriesColorProps = { + categoricalPalette: testCategoricalPalette, + visual: visualOptionSingleSeriesOverride, + muiPrimaryColor: fallbackColor, + seriesName: testSeriesName, + seriesIndex: 0, + querySettings: { + queryIndex: 0, + colorMode: 'fixed-single', + colorValue: '#000', + }, + queryHasMultipleResults: true, + }; + const paletteColor = getSeriesColor(props); + expect(paletteColor).toEqual('hsla(27.48,50%,50%,0.9)'); + }); + + it('should return color set in querySettings when colorMode=fixed & queryHasMultipleResults=false', () => { + const visualOptionSingleSeriesOverride: TimeSeriesChartVisualOptions = { + palette: { + mode: 'auto', + }, + }; + const props: SeriesColorProps = { + categoricalPalette: testCategoricalPalette, + visual: visualOptionSingleSeriesOverride, + muiPrimaryColor: fallbackColor, + seriesName: testSeriesName, + seriesIndex: 0, + querySettings: { + queryIndex: 0, + colorMode: 'fixed-single', + colorValue: '#000', + }, + queryHasMultipleResults: false, + }; + const paletteColor = getSeriesColor(props); + expect(paletteColor).toEqual('#000'); + }); +}); + +describe('getCategoricalPaletteColor', () => { + const fallbackColor = '#ff0000'; + + it('should return 1st color in Categorical palette', () => { + const paletteColor = getCategoricalPaletteColor(['#fff', '000', '#111', '#222', '#333'], 0, fallbackColor); + expect(paletteColor).toEqual('#fff'); + }); + + it('should return 3rd color in Categorical palette', () => { + const paletteColor = getCategoricalPaletteColor(['#fff', '000', '#111', '#222', '#333'], 2, fallbackColor); + expect(paletteColor).toEqual('#111'); + }); + + it('should repeat color after looping through entire palette', () => { + const paletteColor = getCategoricalPaletteColor(['#fff', '000', '#111', '#222', '#333'], 5, fallbackColor); + expect(paletteColor).toEqual('#fff'); + }); +}); + +describe('getAutoPaletteColor', () => { + const fallbackColor = '#ff0000'; + it('should auto generate a color from the series name', () => { + const generatedColor = getAutoPaletteColor('Incoming Writes per second', fallbackColor); + expect(generatedColor).toEqual('hsla(246.82,65%,65%,0.9)'); + }); + + it('should generate a unique color from the series name', () => { + const generatedColor = getAutoPaletteColor( + 'node_memory_Buffers_bytes{env="demo",instance="demo.do.prometheus.io:9100",job="node"}', + fallbackColor + ); + expect(generatedColor).toEqual('hsla(128.97,50%,35%,0.9)'); + }); +}); diff --git a/TimeSeriesChart/src/utils/palette-gen.ts b/TimeSeriesChart/src/utils/palette-gen.ts index f7f80cb..6695b92 100644 --- a/TimeSeriesChart/src/utils/palette-gen.ts +++ b/TimeSeriesChart/src/utils/palette-gen.ts @@ -1,4 +1,4 @@ -// Copyright 2024 The Perses Authors +// Copyright 2023 The Perses Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -11,46 +11,54 @@ // See the License for the specific language governing permissions and // limitations under the License. -import ColorHash from 'color-hash'; -import { TimeSeriesChartVisualOptions } from '../model'; +import { QuerySettingsOptions, TimeSeriesChartVisualOptions } from '../time-series-chart-model'; +import { getConsistentColor } from './palette'; export interface SeriesColorProps { categoricalPalette: string[]; visual: TimeSeriesChartVisualOptions; + muiPrimaryColor: string; seriesName: string; seriesIndex: number; - totalSeries: number; + querySettings?: QuerySettingsOptions; + queryHasMultipleResults?: boolean; } /** - * Get line color as well as color for tooltip and legend, account for whether palette is 'Cateogrical' or 'Auto' (generative) + * Get line color as well as color for tooltip and legend, account for whether palette is 'categorical' or 'auto' aka generative */ -export function getSeriesColor(props: SeriesColorProps) { - const { categoricalPalette, visual, seriesName, seriesIndex, totalSeries } = props; +export function getSeriesColor(props: SeriesColorProps): string { + const { + categoricalPalette, + visual, + muiPrimaryColor, + seriesName, + seriesIndex, + querySettings, + queryHasMultipleResults, + } = props; + + // Use color overrides defined in query settings in priority, if applicable + if (querySettings) { + if (querySettings.colorMode === 'fixed') { + return querySettings.colorValue; + } else if (querySettings.colorMode === 'fixed-single' && !queryHasMultipleResults) { + return querySettings.colorValue; + } + } + // Fallback is unlikely to set unless echarts theme palette in charts theme provider is undefined. const fallbackColor = Array.isArray(categoricalPalette) && categoricalPalette[0] ? (categoricalPalette[0] as string) // Needed since echarts color property isn't always an array. - : 'black'; - - // Explicit way to opt-in to generative palette with consistent colors for same series names. - if (visual.palette?.mode === 'auto') { - return getAutoPaletteColor(seriesName, fallbackColor); - } + : muiPrimaryColor; // Explicit way to always cycle through classical palette instead of changing when based on number of series. if (visual.palette?.mode === 'categorical') { return getCategoricalPaletteColor(categoricalPalette, seriesIndex, fallbackColor); } - // Decide which palette to use based on total series returned. - // When series number exceeds number of colors in palette, use generative colors instead. - const paletteLength = Array.isArray(categoricalPalette) ? categoricalPalette.length : 0; - const seriesColor = - totalSeries <= paletteLength - ? getCategoricalPaletteColor(categoricalPalette, seriesIndex, fallbackColor) - : getAutoPaletteColor(seriesName, fallbackColor); - return seriesColor; + return getAutoPaletteColor(seriesName, fallbackColor); } /** @@ -78,40 +86,9 @@ export function getCategoricalPaletteColor(palette: string[], seriesIndex: numbe return seriesColor; } -// Valid hue values are 0 to 360 and can be adjusted to control the generated colors. -// More info: https://github.com/zenozeng/color-hash#custom-hue -// Picked min of 20 and max of 360 to exclude common threshold colors (red). -// Series names with "error" in them will always be generated as red. -const ERROR_HUE_CUTOFF = 20; -const colorGenerator = new ColorHash({ - hue: { min: ERROR_HUE_CUTOFF, max: 360 }, -}); -const redColorGenerator = new ColorHash({ - hue: { min: 0, max: ERROR_HUE_CUTOFF }, -}); - -// To check whether a color has already been generated for a given string. -// TODO: Predefined color aliases will be defined here -const seriesNameToColorLookup: Record = {}; - /* - * Check whether a color was already generated for a given series name and if not, - * generate a new color (if series name includes 'error', it will have a red hue). + * Generate a consistent series name color (if series name includes 'error', it will have a red hue). */ -export const getConsistentSeriesNameColor = (() => { - return (inputString: string) => { - // Check whether color has already been generated for a given series name. - // Ensures colors are consistent across panels. - if (!seriesNameToColorLookup[inputString]) { - const seriesNameContainsError = inputString.toLowerCase().includes('error'); - const [hue, saturation, lightness] = seriesNameContainsError - ? redColorGenerator.hsl(inputString) - : colorGenerator.hsl(inputString); - const saturationPercent = `${(saturation * 100).toFixed(0)}%`; - const lightnessPercent = `${(lightness * 100).toFixed(0)}%`; - const colorString = `hsla(${hue.toFixed(2)},${saturationPercent},${lightnessPercent},0.9)`; - seriesNameToColorLookup[inputString] = colorString; - } - return seriesNameToColorLookup[inputString]; - }; -})(); +export function getConsistentSeriesNameColor(inputString: string): string { + return getConsistentColor(inputString, inputString.toLowerCase().includes('error')); +} diff --git a/TimeSeriesChart/src/utils/palette.ts b/TimeSeriesChart/src/utils/palette.ts new file mode 100644 index 0000000..a0fa7ed --- /dev/null +++ b/TimeSeriesChart/src/utils/palette.ts @@ -0,0 +1,66 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import ColorHash from 'color-hash'; + +// Valid hue values are 0 to 360 and can be adjusted to control the generated colors. +// More info: https://github.com/zenozeng/color-hash#custom-hue +// Picked min of 20 and max of 360 to exclude common threshold colors (red). +// Items with "error" in them will always be generated as red. +const ERROR_HUE_CUTOFF = 20; +const colorGenerator = new ColorHash({ hue: { min: ERROR_HUE_CUTOFF, max: 360 } }); +const redColorGenerator = new ColorHash({ hue: { min: 0, max: ERROR_HUE_CUTOFF } }); + +function computeConsistentColor(name: string, error: boolean): string { + const [hue, saturation, lightness] = error ? redColorGenerator.hsl(name) : colorGenerator.hsl(name); + const saturationPercent = `${(saturation * 100).toFixed(0)}%`; + const lightnessPercent = `${(lightness * 100).toFixed(0)}%`; + return `hsla(${hue.toFixed(2)},${saturationPercent},${lightnessPercent},0.9)`; +} + +// To check whether a color has already been generated for a given string. +// TODO: Predefined color aliases will be defined here +const colorLookup: Record = {}; + +/** + * Return a consistent color for (name, error) tuple + */ +export function getConsistentColor(name: string, error: boolean): string { + const key = `${name}_____${error}`; + let value = colorLookup[key]; + if (!value) { + value = computeConsistentColor(name, error); + colorLookup[key] = value; + } + return value; +} + +export function getConsistentCategoricalColor( + name: string, + error: boolean, + categoricalPalette: string[], + errorPalette: string[] +): string { + const palette = error ? errorPalette : categoricalPalette; + if (palette.length === 0) { + console.warn('getConsistentCategoricalColor() called with empty color palette, fallback to #000'); + return '#000'; + } + + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = name.charCodeAt(i) + ((hash << 5) - hash); + } + + return palette[Math.abs(hash) % palette.length] ?? '#000'; +} diff --git a/TimeSeriesTable/README.md b/TimeSeriesTable/README.md new file mode 100644 index 0000000..a4a8709 --- /dev/null +++ b/TimeSeriesTable/README.md @@ -0,0 +1,23 @@ +# Perses Panel Plugin + +## Setup + +Install dependencies: + +```bash +npm install +``` + +## Get Started + +Start the dev server: + +```bash +npm run dev +``` + +Build the plugin for distribution: + +```bash +npm run build +``` diff --git a/TimeSeriesTable/jest.config.ts b/TimeSeriesTable/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/TimeSeriesTable/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/TimeSeriesTable/package.json b/TimeSeriesTable/package.json new file mode 100644 index 0000000..672eb13 --- /dev/null +++ b/TimeSeriesTable/package.json @@ -0,0 +1,46 @@ +{ + "name": "@perses-dev/time-series-table", + "private": true, + "version": "0.4.0", + "scripts": { + "dev": "rsbuild dev", + "build": "rsbuild build", + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@module-federation/enhanced": "^0.1.11" + }, + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "@perses-dev/components": "^0.49.0", + "@perses-dev/core": "^0.49.0", + "@perses-dev/plugin-system": "^0.49.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0" + }, + "files": [ + "dist" + ], + "perses": { + "plugins": [ + { + "kind": "Panel", + "spec": { + "display": { + "name": "TimeSeries Table" + }, + "name": "TimeSeriesTable" + } + } + ] + } +} diff --git a/TimeSeriesTable/rsbuild.config.ts b/TimeSeriesTable/rsbuild.config.ts new file mode 100644 index 0000000..807fa8f --- /dev/null +++ b/TimeSeriesTable/rsbuild.config.ts @@ -0,0 +1,59 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack'; +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; + +export default defineConfig({ + server: { + port: 3005, + }, + dev: { + assetPrefix: '/plugins/TimeSeriesTable/', + }, + output: { + assetPrefix: '/plugins/TimeSeriesTable/', + copy: [{ from: './package.json' }, { from: 'README.md' }], + }, + plugins: [pluginReact()], + tools: { + htmlPlugin: false, + rspack: (config, { appendPlugins }) => { + config.output!.uniqueName = 'TimeSeriesTable'; + appendPlugins([ + new ModuleFederationPlugin({ + name: 'TimeSeriesTable', + exposes: { + './TimeSeriesTable': './src/TimeSeriesTable.ts', + }, + shared: { + react: { requiredVersion: '18.2.0', singleton: true }, + 'react-dom': { requiredVersion: '18.2.0', singleton: true }, + echarts: { singleton: true }, + 'date-fns': { singleton: true }, + 'date-fns-tz': { singleton: true }, + lodash: { singleton: true }, + '@perses-dev/components': { singleton: true }, + '@perses-dev/plugin-system': { singleton: true }, + '@emotion/react': { requiredVersion: '^11.11.3', singleton: true }, + '@emotion/styled': { singleton: true }, + '@hookform/resolvers': { singleton: true }, + }, + dts: false, + runtime: false, + }), + ]); + }, + }, +}); diff --git a/TimeSeriesTable/src/DataTable.tsx b/TimeSeriesTable/src/DataTable.tsx new file mode 100644 index 0000000..7c5a3d1 --- /dev/null +++ b/TimeSeriesTable/src/DataTable.tsx @@ -0,0 +1,130 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/* + eslint-disable @typescript-eslint/no-explicit-any + */ +import { Fragment, ReactElement, ReactNode } from 'react'; +import { Alert, Table, TableBody, TableCell, TableHead, TableRow, Typography } from '@mui/material'; +import { TimeSeries, TimeSeriesData } from '@perses-dev/core'; +import { QueryData } from '@perses-dev/plugin-system'; +import { SeriesName } from './SeriesName'; + +const MAX_FORMATABLE_SERIES = 1000; + +export interface DataTableProps { + result: Array>; +} + +/** + * Designed to display timeseries data in a prometheus like table format. + * The first column will contain the metric name and label combination, and the second column will contain the values. + * This is inspired by prometheus DataTable. + * https://github.com/prometheus/prometheus/blob/2524a915915d7eb1b1207152d2e0ce5771193404/web/ui/react-app/src/pages/graph/DataTable.tsx + * @param result timeseries query result + * @constructor + */ +const DataTable = ({ result }: DataTableProps): ReactElement | null => { + if (!result) { + return null; + } + const series = result.flatMap((d) => d.data).flatMap((d) => d?.series || []); + const rows = buildRows(series); + + return ( + <> + {series.length >= MAX_FORMATABLE_SERIES && ( + + Showing more than {MAX_FORMATABLE_SERIES} series, turning off label formatting for performance reasons. + + )} +
    + {rows} +
    + + ); +}; + +function buildRows(series: TimeSeries[]): ReactNode[] { + const isFormatted = series.length < MAX_FORMATABLE_SERIES; // only format series names if we have less than 1000 series for performance reasons + return series.map((s, seriesIdx) => { + const displayTimeStamps = (s.values?.length ?? 0) > 1; + const valuesAndTimes = s.values + ? s.values.map((v, valIdx) => { + return ( + + {v[1]} {displayTimeStamps && @{v[0]}} + + ); + }) + : []; + const histogramsAndTimes = s.histograms + ? s.histograms.map((h: any, hisIdx: number) => { + return ( + + {histogramTable(h[1])} @{h[0]} + + ); + }) + : []; + return ( + + + + + + {valuesAndTimes} + {histogramsAndTimes} + + + ); + }); +} + +const leftDelim = (br: number): string => (br === 3 || br === 1 ? '[' : '('); +const rightDelim = (br: number): string => (br === 3 || br === 0 ? ']' : ')'); + +export const bucketRangeString = ([boundaryRule, leftBoundary, rightBoundary]: [ + number, + string, + string, + string, +]): string => { + return `${leftDelim(boundaryRule)}${leftBoundary} -> ${rightBoundary}${rightDelim(boundaryRule)}`; +}; + +// TODO: add typing to histogram and remove this exception +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const histogramTable = (h: any): ReactNode => ( + + + + + Histogram Sample + + + + + + Range + Count + + {h.buckets?.map((b: any, i: any) => ( + + {bucketRangeString(b)} + {b[3]} + + ))} + +
    +); +export default DataTable; diff --git a/TimeSeriesTable/src/SeriesName.tsx b/TimeSeriesTable/src/SeriesName.tsx new file mode 100644 index 0000000..b2a328b --- /dev/null +++ b/TimeSeriesTable/src/SeriesName.tsx @@ -0,0 +1,93 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Labels } from '@perses-dev/core'; +import { ReactElement, MouseEvent } from 'react'; +import { useSnackbar } from '@perses-dev/components'; +import { Typography } from '@mui/material'; + +interface SeriesNameProps { + name: string; + labels?: Labels; + formattedName?: string; + isFormatted?: boolean; +} + +/* + * Display a series with labels in bold and with a copy to clipboard feature if isFormatted is enabled + * Else it will only display the series in plain text (mostly use for performance reasons) + */ +export function SeriesName({ name, labels, formattedName, isFormatted }: SeriesNameProps): ReactElement { + if (isFormatted && labels && Object.keys(labels).length > 0) { + return ; + } + return {formattedName ?? name}; +} + +function FormatedSeriesName({ labels }: { labels: Labels }): ReactElement { + const { infoSnackbar } = useSnackbar(); + + const labelNodes: ReactElement[] = []; + let first = true; + + function copyToClipboard(e: MouseEvent): void { + const copyText = e.currentTarget.innerText || ''; + navigator.clipboard + .writeText(copyText.trim()) + .then(() => { + infoSnackbar(`Copied label matcher: ${copyText}`); + }) + .catch((reason) => { + console.error(`unable to copy text: ${reason}`); + }); + } + + for (const label in labels) { + if (label === '__name__') { + continue; + } + + labelNodes.push( + + {!first && ', '} + + {label}="{labels[label]}" + + + ); + + if (first) { + first = false; + } + } + + return ( + + {labels ? labels.__name__ : ''} + {'{'} + {labelNodes} + {'}'} + + ); +} diff --git a/TimeSeriesTable/src/TimeSeriesTable.ts b/TimeSeriesTable/src/TimeSeriesTable.ts new file mode 100644 index 0000000..a840b23 --- /dev/null +++ b/TimeSeriesTable/src/TimeSeriesTable.ts @@ -0,0 +1,33 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin, PanelProps } from '@perses-dev/plugin-system'; +import { TimeSeriesTablePanel } from './TimeSeriesTablePanel'; + +interface TimeSeriesTableOptions {} + +/** + * The core TimeSeriesTable panel plugin for Perses. + */ +export const TimeSeriesTable: PanelPlugin = { + PanelComponent: TimeSeriesTablePanel, + supportedQueryTypes: ['TimeSeriesQuery'], + queryOptions: { + mode: 'instant', + }, + createInitialOptions: () => { + return {}; + }, +}; + +export type TimeSeriesTableProps = PanelProps; diff --git a/TimeSeriesTable/src/TimeSeriesTablePanel.test.tsx b/TimeSeriesTable/src/TimeSeriesTablePanel.test.tsx new file mode 100644 index 0000000..3a10eb9 --- /dev/null +++ b/TimeSeriesTable/src/TimeSeriesTablePanel.test.tsx @@ -0,0 +1,141 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ChartsProvider, SnackbarProvider, testChartsTheme } from '@perses-dev/components'; +import { TimeRangeValue, TimeSeriesData, toAbsoluteTimeRange } from '@perses-dev/core'; +import { + MockPlugin, + mockPluginRegistry, + PluginRegistry, + TimeRangeContext, + TimeSeriesQueryPlugin, + useDataQueries, +} from '@perses-dev/plugin-system'; +import { render, screen } from '@testing-library/react'; +import { ReactElement } from 'react'; +import { VirtuosoMockContext } from 'react-virtuoso'; +import { TimeSeriesTableProps } from './model'; +import { + MOCK_TIME_SERIES_DATA_MULTIVALUE, + MOCK_TIME_SERIES_DATA_SINGLEVALUE, + MOCK_TIME_SERIES_QUERY_RESULT_MULTIVALUE, + MOCK_TIME_SERIES_QUERY_RESULT_SINGLEVALUE, +} from './test/mock-query-results'; +import { TimeSeriesTablePanel } from './TimeSeriesTablePanel'; + +jest.mock('@perses-dev/plugin-system', () => { + return { + ...jest.requireActual('@perses-dev/plugin-system'), + useDataQueries: jest.fn(), + }; +}); + +const TEST_TIME_RANGE: TimeRangeValue = { pastDuration: '1h' }; + +function buildFakeTimeSeriesQuery(data: TimeSeriesData): TimeSeriesQueryPlugin { + return { + getTimeSeriesData: async (): Promise => { + return data; + }, + OptionsEditorComponent: (): ReactElement => { + return
    Edit options here
    ; + }, + createInitialOptions: () => ({}), + }; +} + +function buildMockQueryPlugin(data: TimeSeriesData): MockPlugin { + return { + kind: 'PrometheusTimeSeriesQuery', + pluginType: 'TimeSeriesQuery', + plugin: buildFakeTimeSeriesQuery(data), + }; +} + +const TEST_TIME_SERIES_TABLE_PROPS: TimeSeriesTableProps = { + contentDimensions: { + width: 500, + height: 500, + }, + spec: {}, +}; + +describe('TimeSeriesTablePanel', () => { + // Helper to render the panel with some context set + const renderPanel = (data: TimeSeriesData): void => { + const mockTimeRangeContext = { + refreshIntervalInMs: 0, + setRefreshInterval: (): Record => ({}), + timeRange: TEST_TIME_RANGE, + setTimeRange: (): Record => ({}), + absoluteTimeRange: toAbsoluteTimeRange(TEST_TIME_RANGE), + refresh: jest.fn(), + refreshKey: `${TEST_TIME_RANGE.pastDuration}:0`, + }; + + render( + + + + + + + + + + + + ); + }; + + it('should render multi values with timestamps', async () => { + (useDataQueries as jest.Mock).mockReturnValue({ + queryResults: MOCK_TIME_SERIES_QUERY_RESULT_MULTIVALUE, + isLoading: false, + isFetching: false, + }); + renderPanel(MOCK_TIME_SERIES_DATA_MULTIVALUE); + + expect( + screen.getAllByText( + (_, element) => + element?.textContent === + '{device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"}' + ).length + ).toBeGreaterThan(0); + + expect(await screen.findAllByRole('cell')).toHaveLength(4); // 2 lines with 2 column + expect(await screen.findAllByText('@1666479357903')).toHaveLength(2); // first timestamp appear once per line + expect(await screen.findAllByText('@1666479382282')).toHaveLength(2); // second timestamp appear once per line + }); + + it('should render single value without timestamp', async () => { + (useDataQueries as jest.Mock).mockReturnValue({ + queryResults: MOCK_TIME_SERIES_QUERY_RESULT_SINGLEVALUE, + isLoading: false, + isFetching: false, + }); + renderPanel(MOCK_TIME_SERIES_DATA_SINGLEVALUE); + + expect( + screen.getAllByText( + (_, element) => + element?.textContent === + '{device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"}' + ).length + ).toBeGreaterThan(0); + + expect(await screen.findAllByRole('cell')).toHaveLength(4); // 2 lines with 2 column + expect(screen.queryByText('@')).toBeNull(); // No @ as no timestamp + }); +}); diff --git a/TimeSeriesTable/src/TimeSeriesTablePanel.tsx b/TimeSeriesTable/src/TimeSeriesTablePanel.tsx new file mode 100644 index 0000000..456f4f8 --- /dev/null +++ b/TimeSeriesTable/src/TimeSeriesTablePanel.tsx @@ -0,0 +1,36 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { useDataQueries } from '@perses-dev/plugin-system'; +import { Box } from '@mui/material'; +import { LoadingOverlay, useChartsTheme } from '@perses-dev/components'; +import { ReactElement } from 'react'; +import { TimeSeriesTableProps } from './TimeSeriesTable'; +import DataTable from './DataTable'; + +export function TimeSeriesTablePanel(props: TimeSeriesTableProps): ReactElement { + const { contentDimensions } = props; + const chartsTheme = useChartsTheme(); + const { isFetching, isLoading, queryResults } = useDataQueries('TimeSeriesQuery'); + const contentPadding = chartsTheme.container.padding.default; + + if (isLoading || isFetching) { + return ; + } + + return ( + + + + ); +} diff --git a/TimeSeriesTable/src/bootstrap.tsx b/TimeSeriesTable/src/bootstrap.tsx new file mode 100644 index 0000000..3380d4a --- /dev/null +++ b/TimeSeriesTable/src/bootstrap.tsx @@ -0,0 +1,18 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +const root = ReactDOM.createRoot(document.getElementById('root')!); +root.render(); diff --git a/TimeSeriesTable/src/env.d.ts b/TimeSeriesTable/src/env.d.ts new file mode 100644 index 0000000..b302dd9 --- /dev/null +++ b/TimeSeriesTable/src/env.d.ts @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// diff --git a/TimeSeriesTable/src/index.tsx b/TimeSeriesTable/src/index.tsx new file mode 100644 index 0000000..d3d1d92 --- /dev/null +++ b/TimeSeriesTable/src/index.tsx @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import('./bootstrap'); diff --git a/TimeSeriesTable/src/model.ts b/TimeSeriesTable/src/model.ts new file mode 100644 index 0000000..f23a66b --- /dev/null +++ b/TimeSeriesTable/src/model.ts @@ -0,0 +1,5 @@ +import { PanelProps } from '@perses-dev/plugin-system'; + +interface TimeSeriesTableOptions {} + +export type TimeSeriesTableProps = PanelProps; diff --git a/TimeSeriesTable/src/setup-tests.ts b/TimeSeriesTable/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/TimeSeriesTable/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/TimeSeriesTable/src/test/mock-query-results.ts b/TimeSeriesTable/src/test/mock-query-results.ts new file mode 100644 index 0000000..72d2331 --- /dev/null +++ b/TimeSeriesTable/src/test/mock-query-results.ts @@ -0,0 +1,276 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { TimeSeriesData } from '@perses-dev/core'; + +export const MOCK_TIME_SERIES_QUERY_RESULT_MULTIVALUE = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: { + timeRange: { + start: new Date(1666625490000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [ + [1666479357903, 0.27700745551584494], + [1666479382282, 0.27701284657366565], + ], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [ + [1666479357903, 0.08486496097624885], + [1666479382282, 0.08486496097624885], + ], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], + }, + dataUpdatedAt: 1666500979895, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TIME_SERIES_QUERY_RESULT_SINGLEVALUE = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: { + timeRange: { + start: new Date(1666625535000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [[1666479357903, 0.27700745551584494]], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [[1666479357903, 0.08486496097624885]], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], + }, + dataUpdatedAt: 1666500979895, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TIME_SERIES_DATA_MULTIVALUE: TimeSeriesData = { + timeRange: { + start: new Date(1666625490000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [ + [1666479357903, 0.27700745551584494], + [1666479382282, 0.27701284657366565], + ], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [ + [1666479357903, 0.08486496097624885], + [1666479382282, 0.08486496097624885], + ], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], +}; + +export const MOCK_TIME_SERIES_DATA_SINGLEVALUE: TimeSeriesData = { + timeRange: { + start: new Date(1666625535000), + end: new Date(1666625535000), + }, + stepMs: 24379, + series: [ + { + name: 'device="/dev/vda1", env="demo", fstype="ext4", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/"', + values: [[1666479357903, 0.27700745551584494]], + labels: { + device: '/dev/vda1', + env: 'demo', + fstype: 'ext4', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/', + }, + }, + { + name: 'device="/dev/vda15", env="demo", fstype="vfat", instance="demo.do.prometheus.io:9100", job="node", mountpoint="/boot/efi"', + values: [[1666479357903, 0.08486496097624885]], + labels: { + device: '/dev/vda15', + env: 'demo', + fstype: 'vfat', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + mountpoint: '/boot/efi', + }, + }, + ], +}; + +export const MOCK_NULL_QUERY_RESULT = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: { + timeRange: { + start: new Date(1677376110000), + end: new Date(1677376410000), + }, + stepMs: 15000, + series: [ + { + name: 'node_memory_Buffers_bytes{env="demo",instance="demo.do.prometheus.io:9100",job="node"}', + values: [ + [1677376110000, 40000000], + [1677376125000, 40000000], + [1677376140000, null], + [1677376155000, null], + [1677376170000, null], + [1677376185000, null], + [1677376200000, null], + [1677376215000, 40013824], + [1677376230000, 40038400], + [1677376245000, 40054784], + [1677376260000, 40071168], + [1677376275000, 40075264], + [1677376290000, 40091648], + [1677376305000, 40099840], + [1677376320000, 40120320], + [1677376335000, 40128512], + [1677376350000, 40153088], + [1677376365000, 40165376], + [1677376380000, 40177664], + [1677376395000, 40194048], + [1677376410000, 40198144], + ], + formattedName: 'node_memory_Buffers_bytes{env="demo",instance="demo.do.prometheus.io:9100",job="node"}', + labels: { + env: 'demo', + instance: 'demo.do.prometheus.io:9100', + job: 'node', + }, + }, + ], + }, + dataUpdatedAt: 1677376410000, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; diff --git a/TimeSeriesTable/tsconfig.json b/TimeSeriesTable/tsconfig.json new file mode 100644 index 0000000..da01ddb --- /dev/null +++ b/TimeSeriesTable/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.base.json" +} \ No newline at end of file diff --git a/TraceTable/README.md b/TraceTable/README.md new file mode 100644 index 0000000..a4a8709 --- /dev/null +++ b/TraceTable/README.md @@ -0,0 +1,23 @@ +# Perses Panel Plugin + +## Setup + +Install dependencies: + +```bash +npm install +``` + +## Get Started + +Start the dev server: + +```bash +npm run dev +``` + +Build the plugin for distribution: + +```bash +npm run build +``` diff --git a/TraceTable/jest.config.ts b/TraceTable/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/TraceTable/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/TraceTable/package.json b/TraceTable/package.json new file mode 100644 index 0000000..bdaecca --- /dev/null +++ b/TraceTable/package.json @@ -0,0 +1,48 @@ +{ + "name": "@perses-dev/trace-table", + "private": true, + "version": "0.4.0", + "scripts": { + "dev": "rsbuild dev", + "build": "rsbuild build", + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@module-federation/enhanced": "^0.1.11", + "@mui/x-data-grid": "^7.23.1" + }, + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "@perses-dev/components": "^0.49.0", + "@perses-dev/core": "^0.49.0", + "@perses-dev/plugin-system": "^0.49.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "react-router-dom": "^6.11.0", + "use-resize-observer": "^9.0.0" + }, + "files": [ + "dist" + ], + "perses": { + "plugins": [ + { + "kind": "Panel", + "spec": { + "display": { + "name": "Trace Table" + }, + "name": "TraceTable" + } + } + ] + } +} diff --git a/TraceTable/rsbuild.config.ts b/TraceTable/rsbuild.config.ts new file mode 100644 index 0000000..ce62296 --- /dev/null +++ b/TraceTable/rsbuild.config.ts @@ -0,0 +1,60 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack'; +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; + +export default defineConfig({ + server: { + port: 3005, + }, + dev: { + assetPrefix: '/plugins/TraceTable/', + }, + output: { + assetPrefix: '/plugins/TraceTable/', + copy: [{ from: './package.json' }, { from: 'README.md' }], + }, + plugins: [pluginReact()], + tools: { + htmlPlugin: false, + rspack: (config, { appendPlugins }) => { + config.output!.uniqueName = 'TraceTable'; + appendPlugins([ + new ModuleFederationPlugin({ + name: 'TraceTable', + exposes: { + './TraceTable': './src/TraceTable.ts', + }, + shared: { + react: { requiredVersion: '18.2.0', singleton: true }, + 'react-dom': { requiredVersion: '18.2.0', singleton: true }, + 'react-router-dom': { singleton: true }, + echarts: { singleton: true }, + 'date-fns': { singleton: true }, + 'date-fns-tz': { singleton: true }, + lodash: { singleton: true }, + '@perses-dev/components': { singleton: true }, + '@perses-dev/plugin-system': { singleton: true }, + '@emotion/react': { requiredVersion: '^11.11.3', singleton: true }, + '@emotion/styled': { singleton: true }, + '@hookform/resolvers': { singleton: true }, + }, + dts: false, + runtime: false, + }), + ]); + }, + }, +}); diff --git a/TraceTable/src/DataTable.tsx b/TraceTable/src/DataTable.tsx new file mode 100644 index 0000000..84a3c8c --- /dev/null +++ b/TraceTable/src/DataTable.tsx @@ -0,0 +1,235 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Avatar, Box, Chip, Link, Tooltip, Typography, useTheme } from '@mui/material'; +import { + QueryDefinition, + ServiceStats, + TraceData, + TraceSearchResult, + formatDuration, + msToPrometheusDuration, +} from '@perses-dev/core'; +import { QueryData } from '@perses-dev/plugin-system'; +import { Link as RouterLink } from 'react-router-dom'; +import InformationIcon from 'mdi-material-ui/Information'; +import { useChartsTheme } from '@perses-dev/components'; +import { DataGrid, GridColDef } from '@mui/x-data-grid'; +import { ReactElement, useCallback, useMemo } from 'react'; +import { getServiceColor } from './utils/utils'; +import { TraceTableOptions } from './trace-table-model'; + +const DATE_FORMATTER = new Intl.DateTimeFormat(undefined, { + dateStyle: 'long', + timeStyle: 'medium', +}).format; +const UTC_DATE_FORMATTER = new Intl.DateTimeFormat(undefined, { + dateStyle: 'long', + timeStyle: 'long', + timeZone: 'UTC', +}).format; + +export type TraceLink = (params: { query: QueryDefinition; traceId: string }) => string; + +export interface DataTableProps { + options: TraceTableOptions; + result: Array>; + traceLink?: TraceLink; +} + +interface Row extends TraceSearchResult { + id: string; + traceLink?: string; +} + +export function DataTable(props: DataTableProps): ReactElement { + const { options, result, traceLink } = props; + const muiTheme = useTheme(); + const chartsTheme = useChartsTheme(); + + const paletteMode = options.visual?.palette?.mode; + const serviceColorGenerator = useCallback( + (serviceName: string) => getServiceColor(muiTheme, chartsTheme, paletteMode, serviceName), + [muiTheme, chartsTheme, paletteMode] + ); + + const rows: Row[] = []; + for (const query of result) { + for (const trace of query.data?.searchResult || []) { + rows.push({ + ...trace, + id: trace.traceId, + traceLink: traceLink?.({ query: query.definition, traceId: trace.traceId }), + }); + } + } + + const columns = useMemo>>( + () => [ + { + field: 'name', + headerName: 'Trace name', + type: 'string', + flex: 4, + display: 'flex', + valueGetter: (_, trace): string => `${trace.rootServiceName}: ${trace.rootTraceName}`, + renderCell: ({ row }): ReactElement => ( + + +
    + {Object.entries(row.serviceStats).map(([serviceName, stats]) => ( + + ))} +
    + ), + }, + { + field: 'spanCount', + headerName: 'Spans', + type: 'number', + headerAlign: 'left', + align: 'left', + flex: 2, + minWidth: 145, + display: 'flex', + valueGetter: (_, trace) => Object.values(trace.serviceStats).reduce((acc, val) => acc + val.spanCount, 0), + renderCell: ({ row }): ReactElement => { + let totalSpanCount = 0; + let totalErrorCount = 0; + for (const stats of Object.values(row.serviceStats)) { + totalSpanCount += stats.spanCount; + totalErrorCount += stats.errorCount ?? 0; + } + return ( + <> + {totalSpanCount} spans + {totalErrorCount > 0 && ( + } + variant="outlined" + size="small" + color="error" + /> + )} + + ); + }, + }, + { + field: 'durationMs', + headerName: 'Duration', + type: 'number', + headerAlign: 'left', + align: 'left', + flex: 1, + minWidth: 70, + display: 'flex', + renderCell: ({ row }) => ( + + {row.durationMs < 1 ? '<1ms' : formatDuration(msToPrometheusDuration(row.durationMs))} + + ), + }, + { + field: 'startTimeUnixMs', + headerName: 'Start time', + type: 'number', + headerAlign: 'left', + align: 'left', + flex: 3, + minWidth: 240, + display: 'flex', + renderCell: ({ row }) => ( + + {DATE_FORMATTER(new Date(row.startTimeUnixMs))} + + ), + }, + ], + [serviceColorGenerator] + ); + + return ( + 'auto'} + getEstimatedRowHeight={() => 66} + disableRowSelectionOnClick={true} + pageSizeOptions={[10, 20, 50, 100]} + initialState={{ + pagination: { paginationModel: { pageSize: 20 } }, + }} + /> + ); +} + +interface TraceNameProps { + row: Row; +} + +function TraceName({ row: trace }: TraceNameProps): ReactElement { + if (trace.traceLink) { + return ( + + {trace.rootServiceName}: {trace.rootTraceName} + + ); + } + + return ( + + {trace.rootServiceName}: {trace.rootTraceName} + + ); +} + +interface ServiceChipProps { + serviceName: string; + stats: ServiceStats; + serviceColor: string; +} + +function ServiceChip({ serviceName, stats, serviceColor }: ServiceChipProps): ReactElement { + return ( + + {stats.spanCount} + + } + /> + ); +} diff --git a/TraceTable/src/TraceTable.ts b/TraceTable/src/TraceTable.ts new file mode 100644 index 0000000..5fd3aca --- /dev/null +++ b/TraceTable/src/TraceTable.ts @@ -0,0 +1,25 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin } from '@perses-dev/plugin-system'; +import { TraceTablePanel, TraceTablePanelProps } from './TraceTablePanel'; +import { TraceTableOptions, createInitialTraceTableOptions } from './trace-table-model'; + +/** + * The core TraceTable panel plugin for Perses. + */ +export const TraceTable: PanelPlugin = { + PanelComponent: TraceTablePanel, + supportedQueryTypes: ['TraceQuery'], + createInitialOptions: createInitialTraceTableOptions, +}; diff --git a/TraceTable/src/TraceTablePanel.test.tsx b/TraceTable/src/TraceTablePanel.test.tsx new file mode 100644 index 0000000..2b2a44b --- /dev/null +++ b/TraceTable/src/TraceTablePanel.test.tsx @@ -0,0 +1,110 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ChartsProvider, testChartsTheme } from '@perses-dev/components'; +import { TimeRangeValue, toAbsoluteTimeRange } from '@perses-dev/core'; +import { + MockPlugin, + mockPluginRegistry, + PluginRegistry, + TimeRangeContext, + TraceQueryPlugin, + useDataQueries, +} from '@perses-dev/plugin-system'; +import { render, screen } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import { VirtuosoMockContext } from 'react-virtuoso'; +import { MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT } from './test/mock-trace-data'; +import { TraceTablePanel, TraceTablePanelProps } from './TraceTablePanel'; + +jest.mock('@perses-dev/plugin-system', () => { + return { + ...jest.requireActual('@perses-dev/plugin-system'), + useDataQueries: jest.fn(), + }; +}); + +const TEST_TIME_RANGE: TimeRangeValue = { pastDuration: '1h' }; + +function buildFakeTraceQuery(): TraceQueryPlugin { + return { + getTraceData: async (): Promise => { + throw Error('not implemented'); + }, + createInitialOptions: () => ({}), + }; +} + +function buildMockQueryPlugin(): MockPlugin { + return { + kind: 'TempoTraceQuery', + pluginType: 'TraceQuery', + plugin: buildFakeTraceQuery(), + }; +} + +const TEST_TRACE_TABLE_PROPS: TraceTablePanelProps = { + contentDimensions: { + width: 500, + height: 500, + }, + spec: {}, +}; + +describe('TraceTablePanel', () => { + // Helper to render the panel with some context set + const renderPanel = (): void => { + const mockTimeRangeContext = { + refreshIntervalInMs: 0, + setRefreshInterval: (): Record => ({}), + timeRange: TEST_TIME_RANGE, + setTimeRange: (): Record => ({}), + absoluteTimeRange: toAbsoluteTimeRange(TEST_TIME_RANGE), + refresh: jest.fn(), + refreshKey: `${TEST_TIME_RANGE.pastDuration}:0`, + }; + + render( + + + + + + + + + + + + ); + }; + + it('should render multi values with timestamps', async () => { + (useDataQueries as jest.Mock).mockReturnValue({ + queryResults: MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT, + isLoading: false, + isFetching: false, + }); + renderPanel(); + + const rows = screen.getAllByRole('row'); + const lastRow = rows[rows.length - 1]; + expect(lastRow).toHaveTextContent('service-name: span-name'); // trace name + expect(lastRow).toHaveTextContent('10service-name'); // service name s + expect(lastRow).toHaveTextContent('3second-service-name'); // service name s + expect(lastRow).toHaveTextContent('13 spans'); // span count + expect(lastRow).toHaveTextContent('2 errors'); // span count + expect(lastRow).toHaveTextContent('100ms'); // duration + expect(lastRow).toHaveTextContent('December 18, 2023 at 4:07:25 PM'); // start time + }); +}); diff --git a/TraceTable/src/TraceTablePanel.tsx b/TraceTable/src/TraceTablePanel.tsx new file mode 100644 index 0000000..e498c4b --- /dev/null +++ b/TraceTable/src/TraceTablePanel.tsx @@ -0,0 +1,80 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelProps, useDataQueries } from '@perses-dev/plugin-system'; +import { Box } from '@mui/material'; +import { LoadingOverlay, NoDataOverlay, useChartsTheme } from '@perses-dev/components'; +import { QueryDefinition } from '@perses-dev/core'; +import { ReactElement } from 'react'; +import { DataTable, TraceLink } from './DataTable'; +import { TraceTableOptions } from './trace-table-model'; + +export interface TraceTablePanelProps extends PanelProps { + /** + * Specify a link for the traces in the table. + * If this field is unset or undefined, a link to the Gantt chart on the explore page is configured. + * Set this field explicitly to null to disable creating a link. + */ + traceLink?: TraceLink | null; +} + +export function defaultTraceLink({ + query: originalQuery, + traceId, +}: { + query: QueryDefinition; + traceId: string; +}): string { + // clone the original query spec (including the datasource) and replace the query value with the trace id + const query: QueryDefinition = JSON.parse(JSON.stringify(originalQuery)); + query.spec.plugin.spec.query = traceId; + + const traceLinkParams = new URLSearchParams({ + explorer: 'traces', + data: JSON.stringify({ queries: [query] }), + }); + + return `/explore?${traceLinkParams}`; +} + +export function TraceTablePanel(props: TraceTablePanelProps): ReactElement { + const { spec, traceLink } = props; + + const chartsTheme = useChartsTheme(); + const { isFetching, isLoading, queryResults } = useDataQueries('TraceQuery'); + const contentPadding = chartsTheme.container.padding.default; + + if (isLoading || isFetching) { + return ; + } + + const queryError = queryResults.find((d) => d.error); + if (queryError) { + throw queryError.error; + } + + const tracesFound = queryResults.some((traceData) => (traceData.data?.searchResult ?? []).length > 0); + if (!tracesFound) { + return ; + } + + return ( + + + + ); +} diff --git a/TraceTable/src/bootstrap.tsx b/TraceTable/src/bootstrap.tsx new file mode 100644 index 0000000..3380d4a --- /dev/null +++ b/TraceTable/src/bootstrap.tsx @@ -0,0 +1,18 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +const root = ReactDOM.createRoot(document.getElementById('root')!); +root.render(); diff --git a/TraceTable/src/env.d.ts b/TraceTable/src/env.d.ts new file mode 100644 index 0000000..b302dd9 --- /dev/null +++ b/TraceTable/src/env.d.ts @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// diff --git a/TraceTable/src/index.tsx b/TraceTable/src/index.tsx new file mode 100644 index 0000000..d3d1d92 --- /dev/null +++ b/TraceTable/src/index.tsx @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import('./bootstrap'); diff --git a/TraceTable/src/model.ts b/TraceTable/src/model.ts new file mode 100644 index 0000000..6db0923 --- /dev/null +++ b/TraceTable/src/model.ts @@ -0,0 +1,9 @@ +import { Span } from '@perses-dev/core'; + +export interface GanttTrace { + rootSpan: Span; + + // computed properties of the rootSpan + startTimeUnixMs: number; + endTimeUnixMs: number; +} diff --git a/TraceTable/src/setup-tests.ts b/TraceTable/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/TraceTable/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/TraceTable/src/test/get-trace-data-jaeger.ts b/TraceTable/src/test/get-trace-data-jaeger.ts new file mode 100644 index 0000000..5933190 --- /dev/null +++ b/TraceTable/src/test/get-trace-data-jaeger.ts @@ -0,0 +1,157 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Span, SpanEvent, Trace, TraceAttribute, TraceAttributeValue, TraceResource } from '@perses-dev/core'; + +// the following jaeger data types and parsing function should eventually be moved to a jaeger plugin + +export interface JaegerTrace { + traceID: string; + spans: JaegerSpan[]; + processes: unknown; + warnings: unknown; +} + +export interface JaegerSpan { + traceID: string; + spanID: string; + hasChildren: boolean; + childSpanIds: string[]; + depth: number; + processID: string; + process: JaegerProcess; + + operationName: string; + /** start time in microseconds */ + startTime: number; + relativeStartTime: number; + duration: number; + tags: JaegerTag[]; + references: unknown; + logs: unknown; + warnings: unknown; +} + +interface JaegerProcess { + serviceName: string; + tags: JaegerTag[]; +} + +type JaegerTag = + | { + type: 'string'; + key: string; + value: string; + } + | { + type: 'int64'; + key: string; + value: number; + }; + +function parseTag(tags: JaegerTag): TraceAttribute { + let value: TraceAttributeValue; + switch (tags.type) { + case 'string': + value = { stringValue: tags.value }; + break; + case 'int64': + value = { intValue: tags.value.toString() }; + break; + default: + // eslint-disable-next-line @typescript-eslint/no-explicit-any + throw new Error(`unknown jaeger tag type ${(tags as any).type}`); + } + return { key: tags.key, value }; +} + +function parseProcess(process: JaegerProcess): TraceResource { + return { + serviceName: process.serviceName, + attributes: process.tags.map(parseTag), + }; +} + +function parseSpan(span: JaegerSpan): { + traceId: string; + spanId: string; + name: string; + kind: string; + startTimeUnixMs: number; + endTimeUnixMs: number; + attributes: TraceAttribute[]; + events: SpanEvent[]; + status: Record; +} { + return { + traceId: span.traceID, + spanId: span.spanID, + name: span.operationName, + kind: '', + startTimeUnixMs: span.startTime / 1000, + endTimeUnixMs: (span.startTime + span.duration) / 1000, + attributes: span.tags.map(parseTag), + events: [], + status: {}, + }; +} + +export function parseJaegerTrace(jaegerTrace: JaegerTrace): Trace { + // first pass: build lookup table + const lookup = new Map(); + for (const jaegerSpan of jaegerTrace.spans) { + const span: Span = { + resource: parseProcess(jaegerSpan.process), + scope: { + name: jaegerSpan.processID, + }, + childSpans: [], + ...parseSpan(jaegerSpan), + }; + lookup.set(jaegerSpan.spanID, span); + } + + // second pass: build tree based on childSpanIds property + let rootSpan: Span | null = null; + for (const jaegerSpan of jaegerTrace.spans) { + const span = lookup.get(jaegerSpan.spanID); + if (!span) { + continue; + } + + if (jaegerSpan.depth === 0) { + rootSpan = span; + } + + const childSpans: Span[] = []; + for (const childSpanId of jaegerSpan.childSpanIds) { + const childSpan = lookup.get(childSpanId); + if (!childSpan) { + continue; + } + + childSpan.parentSpan = span; + childSpan.parentSpanId = span.spanId; + childSpans.push(childSpan); + } + span.childSpans = childSpans.sort((a, b) => a.startTimeUnixMs - b.startTimeUnixMs); + } + + if (!rootSpan) { + throw new Error('root span not found'); + } + + return { + rootSpan, + }; +} diff --git a/TraceTable/src/test/mock-trace-data.ts b/TraceTable/src/test/mock-trace-data.ts new file mode 100644 index 0000000..e601e2f --- /dev/null +++ b/TraceTable/src/test/mock-trace-data.ts @@ -0,0 +1,885 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Span, Trace, TraceData } from '@perses-dev/core'; +import { GanttTrace } from '../model'; +import { JaegerTrace, parseJaegerTrace } from './get-trace-data-jaeger'; + +function addParentReferences(span: Span): void { + for (const child of span.childSpans) { + child.parentSpan = span; + addParentReferences(child); + } +} + +/** + * Mock data we get from getTraceData() in @perses/tempo-plugin. + */ +export const MOCK_TRACE_SEARCH_RESULT: TraceData = { + searchResult: [ + { + startTimeUnixMs: 1702915645000, // unix epoch time in milliseconds + durationMs: 100, + serviceStats: { + 'service-name': { + spanCount: 10, + }, + 'second-service-name': { + spanCount: 3, + errorCount: 2, + }, + }, + traceId: '123', + rootServiceName: 'service-name', + rootTraceName: 'span-name', + }, + ], + metadata: { + executedQueryString: '{duration > 500ms}', + }, +}; + +export const MOCK_TRACE_SEARCH_RESULT_EMPTY: TraceData = { + searchResult: [], + metadata: { + executedQueryString: '{duration > 500ms}', + }, +}; + +/** + * Mocks results obtained from useTraceQueries() in @perses/plugin-system/runtime. + * This function uses then React TanStack function useQueries(fooQuery) to + * handle fetching. + */ +export const MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: MOCK_TRACE_SEARCH_RESULT, + dataUpdatedAt: 1666500979895, + definition: { + kind: 'TraceQuery', + spec: { + plugin: { + kind: 'TempoTraceQuery', + spec: { + query: '{}', + datasource: { + kind: 'TempoDatasource', + name: 'tempolocal', + }, + }, + }, + }, + }, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT_EMPTY = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: MOCK_TRACE_SEARCH_RESULT_EMPTY, + dataUpdatedAt: 1666500979895, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TRACE: Trace = { + rootSpan: { + resource: { + serviceName: 'shop-backend', + attributes: [{ key: 'service.name', value: { stringValue: 'shop-backend' } }], + }, + scope: { name: 'k6' }, + traceId: 'tid1', + spanId: 'sid1', + name: 'testRootSpan', + kind: 'SPAN_KIND_SERVER', + startTimeUnixMs: 1000, + endTimeUnixMs: 2000, + attributes: [], + events: [], + childSpans: [ + { + resource: { + serviceName: 'shop-backend', + attributes: [{ key: 'service.name', value: { stringValue: 'shop-backend' } }], + }, + scope: { name: 'k6' }, + childSpans: [ + { + resource: { + serviceName: 'shop-backend', + attributes: [{ key: 'service.name', value: { stringValue: 'shop-backend' } }], + }, + scope: { name: 'k6' }, + childSpans: [], + traceId: 'tid1', + spanId: 'sid3', + parentSpanId: 'sid2', + name: 'testChildSpan3', + kind: 'SPAN_KIND_CLIENT', + startTimeUnixMs: 1300, + endTimeUnixMs: 1450, + attributes: [{ key: 'http.method', value: { stringValue: 'PUT' } }], + events: [], + }, + ], + traceId: 'tid1', + spanId: 'sid2', + parentSpanId: 'sid1', + name: 'testChildSpan2', + kind: 'SPAN_KIND_CLIENT', + startTimeUnixMs: 1100, + endTimeUnixMs: 1200, + attributes: [{ key: 'http.method', value: { stringValue: 'DELETE' } }], + events: [ + { + timeUnixMs: 1150, + name: 'event1_name', + attributes: [{ key: 'event1_key', value: { stringValue: 'event1_value' } }], + }, + ], + status: { message: 'Forbidden', code: 'STATUS_CODE_ERROR' }, + }, + ], + }, +}; +addParentReferences(MOCK_TRACE.rootSpan); + +export const MOCK_GANTT_TRACE: GanttTrace = { + rootSpan: MOCK_TRACE.rootSpan, + startTimeUnixMs: MOCK_TRACE.rootSpan.startTimeUnixMs, + endTimeUnixMs: MOCK_TRACE.rootSpan.endTimeUnixMs, +}; + +const MOCK_JAEGER_TRACE_ASYNC: JaegerTrace = { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spans: [ + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '5b6c5367c8f55a07', + operationName: 'post1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599633651, + duration: 3081, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'POST', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path/id-24', + }, + ], + logs: [ + { + timestamp: 1729001599633766, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599636650, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599636673, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 49, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '0bd40fdf749d38af', + operationName: 'get1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599733700, + duration: 2719, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'GET', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path?id=id-24', + }, + ], + logs: [ + { + timestamp: 1729001599733877, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599736343, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599736364, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 100098, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '8a83db29894c10c4', + operationName: 'put1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599833654, + duration: 2803, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'PUT', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path/id-24', + }, + ], + logs: [ + { + timestamp: 1729001599833794, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599836396, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599836408, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 200052, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '4653459b582b47cb', + operationName: 'delete1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599944592, + duration: 20156, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'DELETE', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path/id-24', + }, + ], + logs: [ + { + timestamp: 1729001599944786, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599964619, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599964650, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 310990, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + ], + processes: { + p1: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + }, + warnings: null, +}; + +export const MOCK_TRACE_ASYNC = parseJaegerTrace(MOCK_JAEGER_TRACE_ASYNC); diff --git a/TraceTable/src/trace-table-model.ts b/TraceTable/src/trace-table-model.ts new file mode 100644 index 0000000..1a4c6bd --- /dev/null +++ b/TraceTable/src/trace-table-model.ts @@ -0,0 +1,35 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * The Options object type supported by the TraceTable panel plugin. + */ +// Note: The interface attributes must match cue/schemas/panels/trace-table/trace-table.cue +export interface TraceTableOptions { + visual?: TraceTableVisualOptions; +} + +export interface TraceTableVisualOptions { + palette?: TraceTablePaletteOptions; +} + +export interface TraceTablePaletteOptions { + mode: 'auto' | 'categorical'; +} + +/** + * Creates the initial/empty options for a TraceTable panel. + */ +export function createInitialTraceTableOptions(): TraceTableOptions { + return {}; +} diff --git a/TraceTable/src/utils/palette.ts b/TraceTable/src/utils/palette.ts new file mode 100644 index 0000000..a0fa7ed --- /dev/null +++ b/TraceTable/src/utils/palette.ts @@ -0,0 +1,66 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import ColorHash from 'color-hash'; + +// Valid hue values are 0 to 360 and can be adjusted to control the generated colors. +// More info: https://github.com/zenozeng/color-hash#custom-hue +// Picked min of 20 and max of 360 to exclude common threshold colors (red). +// Items with "error" in them will always be generated as red. +const ERROR_HUE_CUTOFF = 20; +const colorGenerator = new ColorHash({ hue: { min: ERROR_HUE_CUTOFF, max: 360 } }); +const redColorGenerator = new ColorHash({ hue: { min: 0, max: ERROR_HUE_CUTOFF } }); + +function computeConsistentColor(name: string, error: boolean): string { + const [hue, saturation, lightness] = error ? redColorGenerator.hsl(name) : colorGenerator.hsl(name); + const saturationPercent = `${(saturation * 100).toFixed(0)}%`; + const lightnessPercent = `${(lightness * 100).toFixed(0)}%`; + return `hsla(${hue.toFixed(2)},${saturationPercent},${lightnessPercent},0.9)`; +} + +// To check whether a color has already been generated for a given string. +// TODO: Predefined color aliases will be defined here +const colorLookup: Record = {}; + +/** + * Return a consistent color for (name, error) tuple + */ +export function getConsistentColor(name: string, error: boolean): string { + const key = `${name}_____${error}`; + let value = colorLookup[key]; + if (!value) { + value = computeConsistentColor(name, error); + colorLookup[key] = value; + } + return value; +} + +export function getConsistentCategoricalColor( + name: string, + error: boolean, + categoricalPalette: string[], + errorPalette: string[] +): string { + const palette = error ? errorPalette : categoricalPalette; + if (palette.length === 0) { + console.warn('getConsistentCategoricalColor() called with empty color palette, fallback to #000'); + return '#000'; + } + + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = name.charCodeAt(i) + ((hash << 5) - hash); + } + + return palette[Math.abs(hash) % palette.length] ?? '#000'; +} diff --git a/TraceTable/src/utils/utils.ts b/TraceTable/src/utils/utils.ts new file mode 100644 index 0000000..43f933c --- /dev/null +++ b/TraceTable/src/utils/utils.ts @@ -0,0 +1,69 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Span, SpanStatusError } from '@perses-dev/core'; +import { PersesChartsTheme } from '@perses-dev/components'; +import { Theme } from '@mui/material'; +import { getConsistentCategoricalColor, getConsistentColor } from './palette'; + +/** + * Viewport contains the current zoom, i.e. which timeframe of the trace should be visible + */ +export interface Viewport { + startTimeUnixMs: number; + endTimeUnixMs: number; +} + +/** minimum span width, i.e. increase width if the calculated width is too small to be visible */ +export const minSpanWidthPx = 2; +export const rowHeight = '2rem'; +export const spanHasError = (span: Span): boolean => span.status?.code === SpanStatusError; + +export function getServiceColor( + muiTheme: Theme, + chartsTheme: PersesChartsTheme, + paletteMode: 'auto' | 'categorical' | undefined, + serviceName: string, + error = false +): string { + switch (paletteMode) { + case 'categorical': { + // ECharts type for color is not always an array but it is always an array in ChartsProvider + const categoricalPalette = chartsTheme.echartsTheme.color as string[]; + const errorPalette = [muiTheme.palette.error.light, muiTheme.palette.error.main, muiTheme.palette.error.dark]; + return getConsistentCategoricalColor(serviceName, error, categoricalPalette, errorPalette); + } + + default: + return getConsistentColor(serviceName, error); + } +} + +export function getSpanColor( + muiTheme: Theme, + chartsTheme: PersesChartsTheme, + paletteMode: 'auto' | 'categorical' | undefined, + span: Span +): string { + return getServiceColor(muiTheme, chartsTheme, paletteMode, span.resource.serviceName, spanHasError(span)); +} + +export function formatDuration(timeMs: number): string { + if (timeMs < 1) { + return `${Math.round(timeMs * 1000)}μs`; + } + if (timeMs < 1000) { + return `${+timeMs.toFixed(2)}ms`; + } + return `${+(timeMs / 1000).toFixed(2)}s`; +} diff --git a/TraceTable/tsconfig.json b/TraceTable/tsconfig.json new file mode 100644 index 0000000..da01ddb --- /dev/null +++ b/TraceTable/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.base.json" +} \ No newline at end of file diff --git a/TracingGanttChart/README.md b/TracingGanttChart/README.md new file mode 100644 index 0000000..a4a8709 --- /dev/null +++ b/TracingGanttChart/README.md @@ -0,0 +1,23 @@ +# Perses Panel Plugin + +## Setup + +Install dependencies: + +```bash +npm install +``` + +## Get Started + +Start the dev server: + +```bash +npm run dev +``` + +Build the plugin for distribution: + +```bash +npm run build +``` diff --git a/TracingGanttChart/jest.config.ts b/TracingGanttChart/jest.config.ts new file mode 100644 index 0000000..f82da77 --- /dev/null +++ b/TracingGanttChart/jest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Config } from '@jest/types'; +import shared from '../jest.shared'; + +const jestConfig: Config.InitialOptions = { + ...shared, + + setupFilesAfterEnv: [...(shared.setupFilesAfterEnv ?? []), '/src/setup-tests.ts'], +}; + +export default jestConfig; diff --git a/TracingGanttChart/package.json b/TracingGanttChart/package.json new file mode 100644 index 0000000..8150619 --- /dev/null +++ b/TracingGanttChart/package.json @@ -0,0 +1,47 @@ +{ + "name": "@perses-dev/tracing-gantt-chart", + "private": true, + "version": "0.4.0", + "scripts": { + "dev": "rsbuild dev", + "build": "rsbuild build", + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@module-federation/enhanced": "^0.1.11", + "color-hash": "^2.0.2" + }, + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "@perses-dev/components": "^0.49.0", + "@perses-dev/core": "^0.49.0", + "@perses-dev/plugin-system": "^0.49.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0" + }, + "files": [ + "dist" + ], + "perses": { + "plugins": [ + { + "kind": "Panel", + "spec": { + "display": { + "name": "Tracing Gantt Chart" + }, + "name": "TracingGanttChart" + } + } + ] + } +} diff --git a/TracingGanttChart/rsbuild.config.ts b/TracingGanttChart/rsbuild.config.ts new file mode 100644 index 0000000..60d9e91 --- /dev/null +++ b/TracingGanttChart/rsbuild.config.ts @@ -0,0 +1,59 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack'; +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; + +export default defineConfig({ + server: { + port: 3005, + }, + dev: { + assetPrefix: '/plugins/TracingGanttChart/', + }, + output: { + assetPrefix: '/plugins/TracingGanttChart/', + copy: [{ from: './package.json' }, { from: 'README.md' }], + }, + plugins: [pluginReact()], + tools: { + htmlPlugin: false, + rspack: (config, { appendPlugins }) => { + config.output!.uniqueName = 'TracingGanttChart'; + appendPlugins([ + new ModuleFederationPlugin({ + name: 'TracingGanttChart', + exposes: { + './TracingGanttChart': './src/TracingGanttChart.ts', + }, + shared: { + react: { requiredVersion: '18.2.0', singleton: true }, + 'react-dom': { requiredVersion: '18.2.0', singleton: true }, + echarts: { singleton: true }, + 'date-fns': { singleton: true }, + 'date-fns-tz': { singleton: true }, + lodash: { singleton: true }, + '@perses-dev/components': { singleton: true }, + '@perses-dev/plugin-system': { singleton: true }, + '@emotion/react': { requiredVersion: '^11.11.3', singleton: true }, + '@emotion/styled': { singleton: true }, + '@hookform/resolvers': { singleton: true }, + }, + dts: false, + runtime: false, + }), + ]); + }, + }, +}); diff --git a/TracingGanttChart/src/TracingGanttChart.ts b/TracingGanttChart/src/TracingGanttChart.ts new file mode 100644 index 0000000..e39040e --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart.ts @@ -0,0 +1,22 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelPlugin } from '@perses-dev/plugin-system'; +import { createInitialTracingGanttChartOptions, TracingGanttChartOptions } from './gantt-chart-model'; +import { TracingGanttChartPanel, TracingGanttChartPanelProps } from './TracingGanttChartPanel'; + +export const TracingGanttChart: PanelPlugin = { + PanelComponent: TracingGanttChartPanel, + supportedQueryTypes: ['TraceQuery'], + createInitialOptions: createInitialTracingGanttChartOptions, +}; diff --git a/TracingGanttChart/src/TracingGanttChart/DetailPane/Attributes.test.tsx b/TracingGanttChart/src/TracingGanttChart/DetailPane/Attributes.test.tsx new file mode 100644 index 0000000..9c2eb0a --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/DetailPane/Attributes.test.tsx @@ -0,0 +1,74 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { render, RenderResult } from '@testing-library/react'; +import { screen } from '@testing-library/dom'; +import { MemoryRouter } from 'react-router-dom'; +import { TraceAttributeValue } from '@perses-dev/core'; +import { AttributeLinks, AttributeList, AttributeListProps } from './Attributes'; + +describe('Attributes', () => { + const renderComponent = (props: AttributeListProps): RenderResult => { + return render( + + + + ); + }; + + it('render stringValues', () => { + const attributes = [{ key: 'attrkey', value: { stringValue: 'str' } }]; + renderComponent({ attributes }); + expect(screen.getByText('attrkey')).toBeInTheDocument(); + expect(screen.getByText('str')).toBeInTheDocument(); + }); + + it('render intValue', () => { + const attributes = [{ key: 'attrkey', value: { intValue: '123' } }]; + renderComponent({ attributes }); + expect(screen.getByText('123')).toBeInTheDocument(); + }); + + it('render boolValue', () => { + const attributes = [{ key: 'attrkey', value: { boolValue: false } }]; + renderComponent({ attributes }); + expect(screen.getByText('false')).toBeInTheDocument(); + }); + + it('render arrayValue', () => { + const attributes = [ + { key: 'attrkey', value: { arrayValue: { values: [{ stringValue: 'abc' }, { boolValue: true }] } } }, + ]; + renderComponent({ attributes }); + expect(screen.getByText('abc, true')).toBeInTheDocument(); + }); + + it('render an attribute with a link', () => { + const stringValue = (val?: TraceAttributeValue): string => (val && 'stringValue' in val ? val.stringValue : ''); + const attributeLinks: AttributeLinks = { + 'k8s.pod.name': (attrs) => + `/console/ns/${stringValue(attrs['k8s.namespace.name'])}/pod/${stringValue(attrs['k8s.pod.name'])}/detail`, + }; + const attributes = [ + { key: 'k8s.namespace.name', value: { stringValue: 'testing' } }, + { key: 'k8s.pod.name', value: { stringValue: 'hotrod' } }, + ]; + + renderComponent({ attributeLinks, attributes }); + expect(screen.getByText('testing')).not.toHaveAttribute('href'); + expect(screen.getByRole('link', { name: 'hotrod' })).toHaveAttribute( + 'href', + '/console/ns/testing/pod/hotrod/detail' + ); + }); +}); diff --git a/TracingGanttChart/src/TracingGanttChart/DetailPane/Attributes.tsx b/TracingGanttChart/src/TracingGanttChart/DetailPane/Attributes.tsx new file mode 100644 index 0000000..8add94f --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/DetailPane/Attributes.tsx @@ -0,0 +1,80 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ReactElement, useMemo } from 'react'; +import { Link, List, ListItem, ListItemText } from '@mui/material'; +import { Link as RouterLink } from 'react-router-dom'; +import { TraceAttribute, TraceAttributeValue } from '@perses-dev/core'; + +export type AttributeLinks = Record) => string>; + +export interface AttributeListProps { + attributeLinks?: AttributeLinks; + attributes: TraceAttribute[]; +} + +export function AttributeList(props: AttributeListProps): ReactElement { + const { attributeLinks, attributes } = props; + const attributesMap = useMemo( + () => Object.fromEntries(attributes.map((attr) => [attr.key, attr.value])), + [attributes] + ); + + return ( + <> + + {attributes + .sort((a, b) => a.key.localeCompare(b.key)) + .map((attribute, i) => ( + + ))} + + + ); +} + +interface AttributeItemProps { + attribute: TraceAttribute; + linkTo?: string; +} + +function AttributeItem(props: AttributeItemProps): ReactElement { + const { attribute, linkTo } = props; + + const value = linkTo ? ( + + {renderAttributeValue(attribute.value)} + + ) : ( + renderAttributeValue(attribute.value) + ); + + return ( + + + + ); +} + +function renderAttributeValue(value: TraceAttributeValue): string { + if ('stringValue' in value) return value.stringValue.length > 0 ? value.stringValue : ''; + if ('intValue' in value) return value.intValue; + if ('boolValue' in value) return value.boolValue.toString(); + if ('arrayValue' in value) return value.arrayValue.values.map(renderAttributeValue).join(', '); + return 'unknown'; +} diff --git a/TracingGanttChart/src/TracingGanttChart/DetailPane/DetailPane.tsx b/TracingGanttChart/src/TracingGanttChart/DetailPane/DetailPane.tsx new file mode 100644 index 0000000..56d5c9f --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/DetailPane/DetailPane.tsx @@ -0,0 +1,67 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Box, Divider, IconButton, Tab, Tabs, Typography } from '@mui/material'; +import { Span } from '@perses-dev/core'; +import { ReactElement, useState } from 'react'; +import CloseIcon from 'mdi-material-ui/Close'; +import { GanttTrace } from '../trace'; +import { AttributeLinks, AttributeList } from './Attributes'; +import { SpanEventList } from './SpanEvents'; + +export interface DetailPaneProps { + attributeLinks?: AttributeLinks; + trace: GanttTrace; + span: Span; + onCloseBtnClick: () => void; +} + +/** + * DetailPane renders a sidebar showing the span attributes etc. + */ +export function DetailPane(props: DetailPaneProps): ReactElement { + const { attributeLinks, trace, span, onCloseBtnClick } = props; + const [tab, setTab] = useState<'attributes' | 'events'>('attributes'); + + // if the events tab is selected, and then a span without events is clicked, + // we need to switch the current selected tab back to the attributes tab. + if (tab === 'events' && span.events.length === 0) { + setTab('attributes'); + } + + return ( + + + + + {span.resource.serviceName} + + {span.name} + + + setTab(tab)}> + + {span.events.length > 0 && } + + + {tab === 'attributes' && ( + <> + {span.attributes.length > 0 && } + {span.attributes.length > 0 && } + + + )} + {tab === 'events' && } + + ); +} diff --git a/TracingGanttChart/src/TracingGanttChart/DetailPane/SpanEvents.test.tsx b/TracingGanttChart/src/TracingGanttChart/DetailPane/SpanEvents.test.tsx new file mode 100644 index 0000000..6e79c96 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/DetailPane/SpanEvents.test.tsx @@ -0,0 +1,32 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { screen } from '@testing-library/dom'; +import { render, RenderResult } from '@testing-library/react'; +import { MOCK_GANTT_TRACE } from '../../test/mock-trace-data'; +import { SpanEventList, SpanEventListProps } from './SpanEvents'; + +describe('SpanEvents', () => { + const renderComponent = (props: SpanEventListProps): RenderResult => { + return render(); + }; + + it('render', () => { + renderComponent({ trace: MOCK_GANTT_TRACE, span: MOCK_GANTT_TRACE.rootSpan.childSpans[0]! }); + + expect(screen.getByText('150ms')).toBeInTheDocument(); + expect(screen.getByText('event1_name')).toBeInTheDocument(); + expect(screen.getByText('event1_key')).toBeInTheDocument(); + expect(screen.getByText('event1_value')).toBeInTheDocument(); + }); +}); diff --git a/TracingGanttChart/src/TracingGanttChart/DetailPane/SpanEvents.tsx b/TracingGanttChart/src/TracingGanttChart/DetailPane/SpanEvents.tsx new file mode 100644 index 0000000..a96cf02 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/DetailPane/SpanEvents.tsx @@ -0,0 +1,61 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Accordion, AccordionDetails, AccordionSummary, Typography } from '@mui/material'; +import { Span, SpanEvent } from '@perses-dev/core'; +import ExpandMoreIcon from 'mdi-material-ui/ChevronDown'; +import { ReactElement } from 'react'; +import { formatDuration } from '../utils'; +import { GanttTrace } from '../trace'; +import { AttributeList } from './Attributes'; + +export interface SpanEventListProps { + trace: GanttTrace; + span: Span; +} + +export function SpanEventList(props: SpanEventListProps): ReactElement { + const { trace, span } = props; + + return ( + <> + {span.events + .sort((a, b) => a.timeUnixMs - b.timeUnixMs) + .map((event, i) => ( + + ))} + + ); +} + +interface SpanEventItemProps { + trace: GanttTrace; + event: SpanEvent; +} + +function SpanEventItem(props: SpanEventItemProps): ReactElement { + const { trace, event } = props; + const relativeTime = event.timeUnixMs - trace.startTimeUnixMs; + + return ( + + }> + {formatDuration(relativeTime)} + + + {event.name} + + + + ); +} diff --git a/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTable.test.tsx b/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTable.test.tsx new file mode 100644 index 0000000..0673375 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTable.test.tsx @@ -0,0 +1,64 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ChartsProvider, testChartsTheme } from '@perses-dev/components'; +import { fireEvent, screen } from '@testing-library/dom'; +import { render, RenderResult } from '@testing-library/react'; +import { VirtuosoMockContext } from 'react-virtuoso'; +import { MOCK_GANTT_TRACE } from '../../test/mock-trace-data'; +import { GanttTable, GanttTableProps } from './GanttTable'; +import { GanttTableProvider } from './GanttTableProvider'; + +describe('GanttTable', () => { + const renderComponent = (props: Omit): RenderResult => { + const onSpanClick = jest.fn(); + return render( + + + + + + + + ); + }; + + it('render table', () => { + renderComponent({ + options: {}, + trace: MOCK_GANTT_TRACE, + viewport: { + startTimeUnixMs: MOCK_GANTT_TRACE.startTimeUnixMs, + endTimeUnixMs: MOCK_GANTT_TRACE.endTimeUnixMs, + }, + }); + expect(screen.getByText('testRootSpan')).toBeInTheDocument(); + expect(screen.getByText('testChildSpan2')).toBeInTheDocument(); + expect(screen.getByText('testChildSpan3')).toBeInTheDocument(); + }); + + it('collapses a span on click', () => { + renderComponent({ + options: {}, + trace: MOCK_GANTT_TRACE, + viewport: { + startTimeUnixMs: MOCK_GANTT_TRACE.startTimeUnixMs, + endTimeUnixMs: MOCK_GANTT_TRACE.endTimeUnixMs, + }, + }); + fireEvent.click(screen.getAllByTitle('collapse')[1]!); + expect(screen.getByText('testRootSpan')).toBeInTheDocument(); + expect(screen.getByText('testChildSpan2')).toBeInTheDocument(); + expect(screen.queryByText('testChildSpan3')).not.toBeInTheDocument(); + }); +}); diff --git a/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTable.tsx b/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTable.tsx new file mode 100644 index 0000000..58dc1d4 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTable.tsx @@ -0,0 +1,100 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Virtuoso, ListRange } from 'react-virtuoso'; +import { Span } from '@perses-dev/core'; +import { ReactElement, useMemo, useRef, useState } from 'react'; +import { Box, useTheme } from '@mui/material'; +import { Viewport } from '../utils'; +import { TracingGanttChartOptions } from '../../gantt-chart-model'; +import { GanttTrace } from '../trace'; +import { useGanttTableContext } from './GanttTableProvider'; +import { GanttTableRow } from './GanttTableRow'; +import { GanttTableHeader } from './GanttTableHeader'; +import { ResizableDivider } from './ResizableDivider'; + +export interface GanttTableProps { + options: TracingGanttChartOptions; + trace: GanttTrace; + viewport: Viewport; + selectedSpan?: Span; + onSpanClick: (span: Span) => void; +} + +export function GanttTable(props: GanttTableProps): ReactElement { + const { options, trace, viewport, selectedSpan, onSpanClick } = props; + const { collapsedSpans, setVisibleSpans } = useGanttTableContext(); + const [nameColumnWidth, setNameColumnWidth] = useState(0.25); + const tableRef = useRef(null); + const theme = useTheme(); + + const rows = useMemo(() => { + const rows: Span[] = []; + treeToRows(rows, trace.rootSpan, collapsedSpans); + return rows; + }, [trace.rootSpan, collapsedSpans]); + + const divider = ; + + // update currently visible spans + function handleRangeChange({ startIndex, endIndex }: ListRange): void { + const visibleSpans: string[] = []; + for (let i = startIndex; i <= endIndex; i++) { + visibleSpans.push(rows[i]!.spanId); + } + setVisibleSpans(visibleSpans); + } + + return ( + + + ( + + )} + rangeChanged={handleRangeChange} + /> + + ); +} + +/** + * treeToRows recursively transforms the span tree to a list of rows and + * hides collapsed child spans. + */ +function treeToRows(rows: Span[], span: Span, collapsedSpans: string[]): void { + rows.push(span); + if (!collapsedSpans.includes(span.spanId)) { + for (const child of span.childSpans) { + treeToRows(rows, child, collapsedSpans); + } + } +} diff --git a/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableHeader.tsx b/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableHeader.tsx new file mode 100644 index 0000000..5fd8e00 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableHeader.tsx @@ -0,0 +1,50 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Box, Stack, useTheme } from '@mui/material'; +import { ReactElement } from 'react'; +import { Viewport, rowHeight } from '../utils'; +import { TicksHeader } from '../Ticks'; +import { GanttTrace } from '../trace'; + +interface GanttTableHeaderProps { + trace: GanttTrace; + viewport: Viewport; + nameColumnWidth: number; + divider: React.ReactNode; +} + +export function GanttTableHeader(props: GanttTableHeaderProps): ReactElement { + const { trace, viewport, nameColumnWidth, divider } = props; + const theme = useTheme(); + + return ( + + + Service & Operation + + {divider} + + + + + ); +} diff --git a/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableProvider.tsx b/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableProvider.tsx new file mode 100644 index 0000000..15688f8 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableProvider.tsx @@ -0,0 +1,58 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { createContext, ReactElement, useContext, useState } from 'react'; + +interface GanttTableContextType { + collapsedSpans: string[]; + setCollapsedSpans: (s: string[]) => void; + visibleSpans: string[]; + setVisibleSpans: (s: string[]) => void; + /** can be a spanId, an empty string for the root span or undefined for no hover */ + hoveredParent?: string; + setHoveredParent: (s?: string) => void; +} + +/** + * GanttTableContext stores UI state of the rows. + * Required for passing down state to deeply nested , + * without re-rendering intermediate components. + */ +export const GanttTableContext = createContext(undefined); + +interface GanttTableProviderProps { + children?: React.ReactNode; +} + +export function GanttTableProvider(props: GanttTableProviderProps): ReactElement { + const { children } = props; + const [collapsedSpans, setCollapsedSpans] = useState([]); + const [visibleSpans, setVisibleSpans] = useState([]); + const [hoveredParent, setHoveredParent] = useState(undefined); + + return ( + + {children} + + ); +} + +export function useGanttTableContext(): GanttTableContextType { + const ctx = useContext(GanttTableContext); + if (ctx === undefined) { + throw new Error('No GanttTableContext found. Did you forget a Provider?'); + } + return ctx; +} diff --git a/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableRow.tsx b/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableRow.tsx new file mode 100644 index 0000000..2891d7e --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/GanttTable/GanttTableRow.tsx @@ -0,0 +1,63 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Stack, styled, useTheme } from '@mui/material'; +import { Span } from '@perses-dev/core'; +import { memo } from 'react'; +import { Viewport, rowHeight } from '../utils'; +import { TracingGanttChartOptions } from '../../gantt-chart-model'; +import { SpanName } from './SpanName'; +import { SpanDuration } from './SpanDuration'; + +interface GanttTableRowProps { + options: TracingGanttChartOptions; + span: Span; + viewport: Viewport; + selected?: boolean; + nameColumnWidth: number; + divider: React.ReactNode; + onClick: (span: Span) => void; +} + +export const GanttTableRow = memo(function GanttTableRow(props: GanttTableRowProps) { + const { options, span, viewport, selected, nameColumnWidth, divider, onClick } = props; + const theme = useTheme(); + + const handleOnClick = (): void => { + // ignore event if triggered by selecting text + if (document.getSelection()?.type === 'Range') return; + + onClick(span); + }; + + return ( + + + {divider} + + + ); +}); + +const RowContainer = styled(Stack)(({ theme }) => ({ + height: rowHeight, + '&:hover': { + backgroundColor: theme.palette.action.hover, + borderTop: `1px solid ${theme.palette.divider}`, + borderBottom: `1px solid ${theme.palette.divider}`, + }, +})); diff --git a/TracingGanttChart/src/TracingGanttChart/GanttTable/ResizableDivider.tsx b/TracingGanttChart/src/TracingGanttChart/GanttTable/ResizableDivider.tsx new file mode 100644 index 0000000..bffa498 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/GanttTable/ResizableDivider.tsx @@ -0,0 +1,98 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { MouseEvent as ReactMouseEvent, ReactElement, useEffect, useState } from 'react'; +import { Box, styled } from '@mui/material'; +import { useEvent } from '@perses-dev/core'; + +interface ResizableDividerProps { + parentRef: React.RefObject; + spacing?: number; + onMove: (left: number) => void; +} + +export function ResizableDivider(props: ResizableDividerProps): ReactElement { + const { parentRef, spacing = 0, onMove } = props; + const [isResizing, setResizing] = useState(false); + + const handleMouseDown = (e: ReactMouseEvent): void => { + // disable any default actions (text selection, etc.) + e.preventDefault(); + + setResizing(true); + }; + + // need stable reference for window.removeEventListener() in useEffect() below + const handleMouseMove = useEvent((e: MouseEvent) => { + if (!parentRef.current) return; + + const offsetX = e.clientX - parentRef.current.getBoundingClientRect().left + spacing; + const leftPercent = offsetX / parentRef.current.getBoundingClientRect().width; + + if (0.05 <= leftPercent && leftPercent <= 0.95) { + onMove(leftPercent); + } + }); + + // need stable reference for window.removeEventListener() in useEffect() below + const handleMouseUp = useEvent(() => { + setResizing(false); + }); + + // capture mouseMove and mouseUp outside the element by attaching them to the window object + useEffect(() => { + function startMouseAction(): void { + window.addEventListener('mousemove', handleMouseMove); + window.addEventListener('mouseup', handleMouseUp); + document.body.style.cursor = 'col-resize'; + } + + function stopMouseAction(): void { + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('mouseup', handleMouseUp); + document.body.style.cursor = 'inherit'; + } + + if (isResizing) { + startMouseAction(); + } else { + stopMouseAction(); + } + + return stopMouseAction; + }, [isResizing, handleMouseMove, handleMouseUp]); + + // prevent onClick event when clicking on a divider + const stopEventPropagation = (e: ReactMouseEvent): void => e.stopPropagation(); + + return ; +} + +const ResizableDividerBox = styled(Box)(({ theme }) => ({ + position: 'relative', + width: '1px', + height: '100%', + backgroundColor: theme.palette.divider, + cursor: 'col-resize', + + // increase clickable area from 1px to 7px + '&:before': { + position: 'absolute', + width: '7px', + left: '-3px', + top: 0, + bottom: 0, + content: '" "', + zIndex: 1, // without zIndex, the span duration row hides the right side of this element + }, +})); diff --git a/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanDuration.test.tsx b/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanDuration.test.tsx new file mode 100644 index 0000000..7727b3c --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanDuration.test.tsx @@ -0,0 +1,71 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ChartsProvider, testChartsTheme } from '@perses-dev/components'; +import { screen } from '@testing-library/dom'; +import { render, RenderResult } from '@testing-library/react'; +import { MOCK_GANTT_TRACE } from '../../test/mock-trace-data'; +import { GanttTableProvider } from './GanttTableProvider'; +import { SpanDuration, SpanDurationProps } from './SpanDuration'; + +describe('SpanDuration', () => { + const renderComponent = (props: SpanDurationProps): RenderResult => { + return render( + + + + + + ); + }; + + it('render span bar', () => { + renderComponent({ + options: {}, + span: MOCK_GANTT_TRACE.rootSpan.childSpans[0]!.childSpans[0]!, + viewport: { + startTimeUnixMs: MOCK_GANTT_TRACE.startTimeUnixMs, + endTimeUnixMs: MOCK_GANTT_TRACE.endTimeUnixMs, + }, + }); + expect(screen.getByText('150ms')).toBeInTheDocument(); + expect(parseInt(screen.getByText('150ms').style.left)).toEqual(44); // 44%, on the right side of the span bar + expect(screen.getByTestId('span-duration-bar').style.backgroundColor).toEqual('rgba(83, 83, 83, 0.9)'); + }); + + it('render span bar duration on left side', () => { + renderComponent({ + options: {}, + span: MOCK_GANTT_TRACE.rootSpan.childSpans[0]!.childSpans[0]!, + viewport: { + startTimeUnixMs: MOCK_GANTT_TRACE.startTimeUnixMs + 290, + endTimeUnixMs: MOCK_GANTT_TRACE.startTimeUnixMs + 400, + }, + }); + expect(screen.getByText('150ms')).toBeInTheDocument(); + expect(parseInt(screen.getByText('150ms').style.left)).toEqual(9); // 9%, on the left side of the span bar + }); + + it('render span bar with colors from eCharts theme', () => { + renderComponent({ + options: { visual: { palette: { mode: 'categorical' } } }, + span: MOCK_GANTT_TRACE.rootSpan.childSpans[0]!.childSpans[0]!, + viewport: { + startTimeUnixMs: MOCK_GANTT_TRACE.startTimeUnixMs, + endTimeUnixMs: MOCK_GANTT_TRACE.endTimeUnixMs, + }, + }); + expect(screen.getByText('150ms')).toBeInTheDocument(); + expect(screen.getByTestId('span-duration-bar').style.backgroundColor).toEqual('rgb(0, 114, 178)'); // #0072B2 from Perses color palette (theme-gen.ts) + }); +}); diff --git a/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanDuration.tsx b/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanDuration.tsx new file mode 100644 index 0000000..bd2fbd8 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanDuration.tsx @@ -0,0 +1,86 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Box, useTheme } from '@mui/material'; +import { Span } from '@perses-dev/core'; +import { useChartsTheme } from '@perses-dev/components'; +import { ReactElement } from 'react'; +import { Viewport, formatDuration, getSpanColor, minSpanWidthPx } from '../utils'; +import { Ticks } from '../Ticks'; +import { TracingGanttChartOptions } from '../../gantt-chart-model'; + +export interface SpanDurationProps { + options: TracingGanttChartOptions; + span: Span; + viewport: Viewport; +} + +/** + * SpanDuration renders the right column of a SpanRow, i.e. the span bar and span duration + */ +export function SpanDuration(props: SpanDurationProps): ReactElement { + const { options, span, viewport } = props; + const muiTheme = useTheme(); + const chartsTheme = useChartsTheme(); + + const spanDuration = span.endTimeUnixMs - span.startTimeUnixMs; + const viewportDuration = viewport.endTimeUnixMs - viewport.startTimeUnixMs; + const relativeDuration = spanDuration / viewportDuration; + const relativeStart = (span.startTimeUnixMs - viewport.startTimeUnixMs) / viewportDuration; + + return ( + + + + + {formatDuration(spanDuration)} + + + ); +} diff --git a/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanIndents.tsx b/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanIndents.tsx new file mode 100644 index 0000000..b0ad816 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanIndents.tsx @@ -0,0 +1,99 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { styled, useTheme } from '@mui/material'; +import ChevronDownIcon from 'mdi-material-ui/ChevronDown'; +import ChevronRightIcon from 'mdi-material-ui/ChevronRight'; +import { MouseEvent, ReactElement, useCallback } from 'react'; +import { Span } from '@perses-dev/core'; +import { useGanttTableContext } from './GanttTableProvider'; + +const MIN_INDENT_WIDTH = 8; +const MAX_INDENT_WIDTH = 24; + +export interface SpanIndentsProps { + span: Span; +} + +/** + * SpanIndents renders the indention boxes, + * and handles the click and mouseOver events + * + * Note: This component gets re-rendered on every hover of any indention box, + * therefore rendering performance is essential. + */ +export function SpanIndents(props: SpanIndentsProps): ReactElement { + const { span } = props; + const { collapsedSpans, setCollapsedSpans, visibleSpans, hoveredParent, setHoveredParent } = useGanttTableContext(); + const theme = useTheme(); + + const handleToggleClick = useCallback( + (e: MouseEvent) => { + e.stopPropagation(); + if (collapsedSpans.includes(span.spanId)) { + setCollapsedSpans(collapsedSpans.filter((spanId) => spanId !== span.spanId)); + } else { + setCollapsedSpans([...collapsedSpans, span.spanId]); + } + }, + [span, collapsedSpans, setCollapsedSpans] + ); + + const handleIconMouseEnter = useCallback(() => { + setHoveredParent(span.spanId); + }, [span, setHoveredParent]); + + const spans = [span]; + let parent = span.parentSpan; + while (parent) { + spans.unshift(parent); + parent = parent.parentSpan; + } + + // on first render visibleSpans is empty, therefore let's use MAX_INDENT_WIDTH to avoid an animation on page load. + return ( + <> + {spans.map((span, i) => ( + setHoveredParent(span.parentSpanId ?? '')} + onMouseLeave={() => setHoveredParent(undefined)} + > + {i === spans.length - 1 && + span.childSpans.length > 0 && + (collapsedSpans.includes(span.spanId) ? ( + + ) : ( + + ))} + + ))} + + ); +} + +const SpanIndentBox = styled('div')({ + display: 'flex', + height: '100%', + alignItems: 'center', + justifyContent: 'flex-end', + flexShrink: 0, + transition: 'width 1s', +}); diff --git a/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanName.test.tsx b/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanName.test.tsx new file mode 100644 index 0000000..c77d65a --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanName.test.tsx @@ -0,0 +1,40 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { screen } from '@testing-library/dom'; +import { render, RenderResult } from '@testing-library/react'; +import { MOCK_GANTT_TRACE } from '../../test/mock-trace-data'; +import { GanttTableProvider } from './GanttTableProvider'; +import { SpanName, SpanNameProps } from './SpanName'; + +describe('SpanName', () => { + const renderComponent = (props: Omit): RenderResult => { + return render( + + + + ); + }; + + it('render span name without error', () => { + renderComponent({ span: MOCK_GANTT_TRACE.rootSpan.childSpans[0]!.childSpans[0]! }); + expect(screen.getByText('testChildSpan3')).toBeInTheDocument(); + expect(screen.queryByText('error')).not.toBeInTheDocument(); + }); + + it('render span name with error', () => { + renderComponent({ span: MOCK_GANTT_TRACE.rootSpan.childSpans[0]! }); + expect(screen.getByText('testChildSpan2')).toBeInTheDocument(); + expect(screen.getByText('error')).toBeInTheDocument(); + }); +}); diff --git a/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanName.tsx b/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanName.tsx new file mode 100644 index 0000000..ca53e7d --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/GanttTable/SpanName.tsx @@ -0,0 +1,41 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Box, Stack } from '@mui/material'; +import { Span } from '@perses-dev/core'; +import AlertIcon from 'mdi-material-ui/AlertCircleOutline'; +import { ReactElement } from 'react'; +import { spanHasError } from '../utils'; +import { SpanIndents } from './SpanIndents'; + +export interface SpanNameProps { + span: Span; + nameColumnWidth: number; +} + +/** + * SpanName renders the entire left column of a SpanRow, i.e. the hierarchy and the service and span name + */ +export function SpanName(props: SpanNameProps): ReactElement { + const { span, nameColumnWidth } = props; + + return ( + + + {spanHasError(span) && } + + {span.resource.serviceName}: {span.name} + + + ); +} diff --git a/TracingGanttChart/src/TracingGanttChart/MiniGanttChart/Canvas.tsx b/TracingGanttChart/src/TracingGanttChart/MiniGanttChart/Canvas.tsx new file mode 100644 index 0000000..6f1f194 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/MiniGanttChart/Canvas.tsx @@ -0,0 +1,219 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Box, styled, useTheme } from '@mui/material'; +import useResizeObserver from 'use-resize-observer'; +import { useEffect, useRef, MouseEvent as ReactMouseEvent, useState, useCallback, ReactElement } from 'react'; +import { Span, useEvent } from '@perses-dev/core'; +import { useChartsTheme } from '@perses-dev/components'; +import { Ticks } from '../Ticks'; +import { getSpanColor, Viewport } from '../utils'; +import { TracingGanttChartOptions } from '../../gantt-chart-model'; +import { GanttTrace } from '../trace'; +import { drawSpans } from './draw'; + +const CANVAS_HEIGHT = 60; + +interface CanvasProps { + options: TracingGanttChartOptions; + trace: GanttTrace; + viewport: Viewport; + setViewport: (v: Viewport) => void; +} + +type MouseState = + | { type: 'none' } + | { type: 'resize'; fixedPoint: number } + | { type: 'drag'; start: number; end: number }; + +export function Canvas(props: CanvasProps): ReactElement { + const { options, trace, viewport, setViewport } = props; + const muiTheme = useTheme(); + const chartsTheme = useChartsTheme(); + // the element must have an absolute width and height to avoid rendering problems + // the wrapper box is required to get the available dimensions for the element + const { width, ref: wrapperRef } = useResizeObserver(); + const height = CANVAS_HEIGHT; + const canvasRef = useRef(null); + const [mouseState, setMouseState] = useState({ type: 'none' }); + + const traceDuration = trace.endTimeUnixMs - trace.startTimeUnixMs; + const relativeCutoffLeft = (viewport.startTimeUnixMs - trace.startTimeUnixMs) / traceDuration; + const relativeCutoffRight = (trace.endTimeUnixMs - viewport.endTimeUnixMs) / traceDuration; + + const spanColorGenerator = useCallback( + (span: Span) => getSpanColor(muiTheme, chartsTheme, options.visual?.palette?.mode, span), + [muiTheme, chartsTheme, options.visual?.palette?.mode] + ); + + useEffect(() => { + if (!canvasRef.current || !width || !height) return; + + const ctx = canvasRef.current.getContext('2d'); + if (!ctx) return; + + drawSpans(ctx, width, height, trace, spanColorGenerator); + }, [width, height, trace, spanColorGenerator]); + + const translateCursorToTime = (e: ReactMouseEvent | MouseEvent): number => { + if (!canvasRef.current || !width) return 0; + // e.nativeEvent.offsetX doesn't work when sliding over a tick box + const offsetX = e.clientX - canvasRef.current.getBoundingClientRect().left; + return trace.startTimeUnixMs + (offsetX / width) * traceDuration; + }; + + const handleMouseDown = (e: ReactMouseEvent): void => { + e.preventDefault(); + if (!(e.target instanceof HTMLElement)) return; + + const isDefaultViewport = + viewport.startTimeUnixMs === trace.startTimeUnixMs && viewport.endTimeUnixMs === trace.endTimeUnixMs; + const elem = e.target.dataset['elem']; + const cursor = translateCursorToTime(e); + + if (elem === 'resizerLeft') { + setMouseState({ type: 'resize', fixedPoint: viewport.endTimeUnixMs }); + } else if (elem === 'resizerRight') { + setMouseState({ type: 'resize', fixedPoint: viewport.startTimeUnixMs }); + } else if (elem === 'cutoffBox' || isDefaultViewport) { + setMouseState({ type: 'resize', fixedPoint: cursor }); + setViewport({ startTimeUnixMs: cursor, endTimeUnixMs: cursor }); + } else { + setMouseState({ + type: 'drag', + start: cursor - viewport.startTimeUnixMs, + end: viewport.endTimeUnixMs - cursor, + }); + } + }; + + // need stable reference for window.removeEventListener() in useEffect() below + const handleMouseMove = useEvent((e: MouseEvent) => { + e.preventDefault(); + + switch (mouseState.type) { + case 'none': + return; + + case 'resize': { + const pointA = mouseState.fixedPoint; + const pointB = translateCursorToTime(e); + + let start, end; + if (pointA < pointB) { + start = pointA; + end = pointB; + } else { + start = pointB; + end = pointA; + } + + setViewport({ + startTimeUnixMs: Math.max(start, trace.startTimeUnixMs), + endTimeUnixMs: Math.min(end, trace.endTimeUnixMs), + }); + return; + } + + case 'drag': { + // avoid using e.movementX here, as it skips events in chrome, + // resulting in the mouse pointer moving faster than the viewport box + const { start, end } = mouseState; + let cursor = translateCursorToTime(e); + + if (cursor - start < trace.startTimeUnixMs) { + cursor = trace.startTimeUnixMs + start; + } + if (cursor + end > trace.endTimeUnixMs) { + cursor = trace.endTimeUnixMs - end; + } + + setViewport({ + startTimeUnixMs: cursor - start, + endTimeUnixMs: cursor + end, + }); + return; + } + } + }); + + // need stable reference for window.removeEventListener() in useEffect() below + const handleMouseUp = useEvent((e: MouseEvent) => { + e.preventDefault(); + setMouseState({ type: 'none' }); + + // reset viewport if start === end, i.e. a click without movement + if (viewport.startTimeUnixMs === viewport.endTimeUnixMs) { + setViewport({ startTimeUnixMs: trace.startTimeUnixMs, endTimeUnixMs: trace.endTimeUnixMs }); + } + }); + + // capture mouseMove and mouseUp outside the element by attaching them to the window object + useEffect(() => { + function startMouseAction(): void { + window.addEventListener('mousemove', handleMouseMove); + window.addEventListener('mouseup', handleMouseUp); + document.body.style.cursor = mouseState.type === 'resize' ? 'col-resize' : 'move'; + } + + function stopMouseAction(): void { + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('mouseup', handleMouseUp); + document.body.style.cursor = 'inherit'; + } + + if (mouseState.type === 'none') { + stopMouseAction(); + } else { + startMouseAction(); + } + + return stopMouseAction; + }, [mouseState, handleMouseMove, handleMouseUp]); + + return ( + + + + + + + + + ); +} + +const CutoffBox = styled(Box)({ + position: 'absolute', + height: '100%', + backgroundColor: 'rgba(225, 225, 225, .5)', +}); + +const Resizer = styled(Box)(({ theme }) => ({ + position: 'absolute', + height: '100%', + backgroundColor: theme.palette.divider, + width: '2px', + cursor: 'col-resize', + + // increase clickable area from 2px to 8px + '&:before': { + position: 'absolute', + width: '8px', + left: '-3px', + top: 0, + bottom: 0, + content: '" "', + zIndex: 1, // without zIndex, the cutoff boxes partially hide this element + }, +})); diff --git a/TracingGanttChart/src/TracingGanttChart/MiniGanttChart/MiniGanttChart.tsx b/TracingGanttChart/src/TracingGanttChart/MiniGanttChart/MiniGanttChart.tsx new file mode 100644 index 0000000..6e391f1 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/MiniGanttChart/MiniGanttChart.tsx @@ -0,0 +1,49 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Box, useTheme } from '@mui/material'; +import { ReactElement } from 'react'; +import { TicksHeader } from '../Ticks'; +import { Viewport, rowHeight } from '../utils'; +import { TracingGanttChartOptions } from '../../gantt-chart-model'; +import { GanttTrace } from '../trace'; +import { Canvas } from './Canvas'; + +interface MiniGanttChartProps { + options: TracingGanttChartOptions; + trace: GanttTrace; + viewport: Viewport; + setViewport: (v: Viewport) => void; +} + +export function MiniGanttChart(props: MiniGanttChartProps): ReactElement { + const { options, trace, viewport, setViewport } = props; + const theme = useTheme(); + + return ( + + + + + + + ); +} diff --git a/TracingGanttChart/src/TracingGanttChart/MiniGanttChart/draw.ts b/TracingGanttChart/src/TracingGanttChart/MiniGanttChart/draw.ts new file mode 100644 index 0000000..e3a8255 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/MiniGanttChart/draw.ts @@ -0,0 +1,66 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Span } from '@perses-dev/core'; +import { GanttTrace } from '../trace'; +import { minSpanWidthPx } from '../utils'; + +const MIN_BAR_HEIGHT = 1; +const MAX_BAR_HEIGHT = 7; + +function countSpans(span: Span): number { + let n = 1; + for (const childSpan of span.childSpans) { + n += countSpans(childSpan); + } + return n; +} + +export function drawSpans( + ctx: CanvasRenderingContext2D, + width: number, + height: number, + trace: GanttTrace, + spanColorGenerator: (span: Span) => string +): void { + // calculate optimal height, enforce min and max bar height and finally round to an integer + const numSpans = countSpans(trace.rootSpan); + const barHeight = Math.round(Math.min(Math.max(height / numSpans, MIN_BAR_HEIGHT), MAX_BAR_HEIGHT)); + + const traceDuration = trace.endTimeUnixMs - trace.startTimeUnixMs; + const yChange = height / numSpans; + let y = 0; + + const drawSpan = (span: Span): void => { + const spanDuration = span.endTimeUnixMs - span.startTimeUnixMs; + const relativeDuration = spanDuration / traceDuration; + const relativeStart = (span.startTimeUnixMs - trace.startTimeUnixMs) / traceDuration; + + ctx.fillStyle = spanColorGenerator(span); + ctx.beginPath(); + ctx.rect( + Math.round(relativeStart * width), + Math.round(y), + Math.max(minSpanWidthPx, Math.round(relativeDuration * width)), + barHeight + ); + ctx.fill(); + y += yChange; + + for (const childSpan of span.childSpans) { + drawSpan(childSpan); + } + }; + + drawSpan(trace.rootSpan); +} diff --git a/TracingGanttChart/src/TracingGanttChart/Ticks.test.tsx b/TracingGanttChart/src/TracingGanttChart/Ticks.test.tsx new file mode 100644 index 0000000..dfd6458 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/Ticks.test.tsx @@ -0,0 +1,35 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { screen } from '@testing-library/dom'; +import { render, RenderResult } from '@testing-library/react'; +import { MOCK_GANTT_TRACE } from '../test/mock-trace-data'; +import { TicksHeader, TicksHeaderProps } from './Ticks'; + +describe('Ticks', () => { + const renderComponent = (props: TicksHeaderProps): RenderResult => { + return render(); + }; + + it('render ', () => { + renderComponent({ + trace: MOCK_GANTT_TRACE, + viewport: { startTimeUnixMs: MOCK_GANTT_TRACE.startTimeUnixMs, endTimeUnixMs: MOCK_GANTT_TRACE.endTimeUnixMs }, + }); + expect(screen.getByText('0μs')).toBeInTheDocument(); + expect(screen.getByText('250ms')).toBeInTheDocument(); + expect(screen.getByText('500ms')).toBeInTheDocument(); + expect(screen.getByText('750ms')).toBeInTheDocument(); + expect(screen.getByText('1s')).toBeInTheDocument(); + }); +}); diff --git a/TracingGanttChart/src/TracingGanttChart/Ticks.tsx b/TracingGanttChart/src/TracingGanttChart/Ticks.tsx new file mode 100644 index 0000000..197c5e8 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/Ticks.tsx @@ -0,0 +1,64 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Box, styled } from '@mui/material'; +import { ReactElement } from 'react'; +import { Viewport, formatDuration } from './utils'; +import { GanttTrace } from './trace'; + +export interface TicksHeaderProps { + trace: GanttTrace; + viewport: Viewport; +} + +/** + * TicksHeader renders all tick labels in the header + */ +export function TicksHeader(props: TicksHeaderProps): ReactElement { + const { trace, viewport } = props; + + const duration = viewport.endTimeUnixMs - viewport.startTimeUnixMs; + const startAt = viewport.startTimeUnixMs - trace.startTimeUnixMs; + + return ( + <> + {formatDuration(startAt + duration * 0)} + {formatDuration(startAt + duration * 0.25)} + {formatDuration(startAt + duration * 0.5)} + {formatDuration(startAt + duration * 0.75)} + + {formatDuration(startAt + duration * 1)} + + + ); +} + +/** + * Ticks renders all ticks in the span duration + */ +export function Ticks(): ReactElement { + return ( + <> + + + + + ); +} + +const TickBox = styled(Box)(({ theme }) => ({ + position: 'absolute', + height: '100%', + borderLeft: `1px solid ${theme.palette.divider}`, + padding: '.25rem', +})); diff --git a/TracingGanttChart/src/TracingGanttChart/TracingGanttChart.tsx b/TracingGanttChart/src/TracingGanttChart/TracingGanttChart.tsx new file mode 100644 index 0000000..affd2b1 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/TracingGanttChart.tsx @@ -0,0 +1,88 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ReactElement, useMemo, useRef, useState } from 'react'; +import { Box, Stack, useTheme } from '@mui/material'; +import { Span, Trace } from '@perses-dev/core'; +import { TracingGanttChartOptions } from '../gantt-chart-model'; +import { MiniGanttChart } from './MiniGanttChart/MiniGanttChart'; +import { DetailPane } from './DetailPane/DetailPane'; +import { Viewport } from './utils'; +import { GanttTable } from './GanttTable/GanttTable'; +import { GanttTableProvider } from './GanttTable/GanttTableProvider'; +import { ResizableDivider } from './GanttTable/ResizableDivider'; +import { AttributeLinks } from './DetailPane/Attributes'; +import { getTraceModel } from './trace'; + +export interface TracingGanttChartProps { + options: TracingGanttChartOptions; + attributeLinks?: AttributeLinks; + trace: Trace; +} + +/** + * The core GanttChart panel for Perses. + * + * The UI/UX of this panel is based on Jaeger UI, licensed under Apache License, Version 2.0. + * https://github.com/jaegertracing/jaeger-ui + */ +export function TracingGanttChart(props: TracingGanttChartProps): ReactElement { + const { options, attributeLinks, trace: coreTrace } = props; + + const theme = useTheme(); + const trace = useMemo(() => { + // calculate (and memoize) common properties, for example start and end time of the trace + return getTraceModel(coreTrace); + }, [coreTrace]); + const [viewport, setViewport] = useState({ + startTimeUnixMs: trace.startTimeUnixMs, + endTimeUnixMs: trace.endTimeUnixMs, + }); + const [selectedSpan, setSelectedSpan] = useState(undefined); + + const ganttChart = useRef(null); + // tableWidth only comes to effect if the detail pane is visible. + // setTableWidth() is only called by + const [tableWidth, setTableWidth] = useState(0.82); + const gap = 2; + + return ( + + + + + + + + {selectedSpan && ( + <> + + + setSelectedSpan(undefined)} + /> + + + )} + + ); +} diff --git a/TracingGanttChart/src/TracingGanttChart/palette.ts b/TracingGanttChart/src/TracingGanttChart/palette.ts new file mode 100644 index 0000000..a0fa7ed --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/palette.ts @@ -0,0 +1,66 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import ColorHash from 'color-hash'; + +// Valid hue values are 0 to 360 and can be adjusted to control the generated colors. +// More info: https://github.com/zenozeng/color-hash#custom-hue +// Picked min of 20 and max of 360 to exclude common threshold colors (red). +// Items with "error" in them will always be generated as red. +const ERROR_HUE_CUTOFF = 20; +const colorGenerator = new ColorHash({ hue: { min: ERROR_HUE_CUTOFF, max: 360 } }); +const redColorGenerator = new ColorHash({ hue: { min: 0, max: ERROR_HUE_CUTOFF } }); + +function computeConsistentColor(name: string, error: boolean): string { + const [hue, saturation, lightness] = error ? redColorGenerator.hsl(name) : colorGenerator.hsl(name); + const saturationPercent = `${(saturation * 100).toFixed(0)}%`; + const lightnessPercent = `${(lightness * 100).toFixed(0)}%`; + return `hsla(${hue.toFixed(2)},${saturationPercent},${lightnessPercent},0.9)`; +} + +// To check whether a color has already been generated for a given string. +// TODO: Predefined color aliases will be defined here +const colorLookup: Record = {}; + +/** + * Return a consistent color for (name, error) tuple + */ +export function getConsistentColor(name: string, error: boolean): string { + const key = `${name}_____${error}`; + let value = colorLookup[key]; + if (!value) { + value = computeConsistentColor(name, error); + colorLookup[key] = value; + } + return value; +} + +export function getConsistentCategoricalColor( + name: string, + error: boolean, + categoricalPalette: string[], + errorPalette: string[] +): string { + const palette = error ? errorPalette : categoricalPalette; + if (palette.length === 0) { + console.warn('getConsistentCategoricalColor() called with empty color palette, fallback to #000'); + return '#000'; + } + + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = name.charCodeAt(i) + ((hash << 5) - hash); + } + + return palette[Math.abs(hash) % palette.length] ?? '#000'; +} diff --git a/TracingGanttChart/src/TracingGanttChart/trace.test.ts b/TracingGanttChart/src/TracingGanttChart/trace.test.ts new file mode 100644 index 0000000..bcb5274 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/trace.test.ts @@ -0,0 +1,29 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { MOCK_TRACE, MOCK_TRACE_ASYNC } from '../test/mock-trace-data'; +import { getTraceModel } from './trace'; + +describe('trace', () => { + it('computes a GanttTrace model from a trace', (): void => { + const ganttTrace = getTraceModel(MOCK_TRACE); + expect(ganttTrace.startTimeUnixMs).toEqual(1000); + expect(ganttTrace.endTimeUnixMs).toEqual(2000); + }); + + it('computes a GanttTrace model from a trace where trace duration != root span duration', () => { + const ganttTrace = getTraceModel(MOCK_TRACE_ASYNC); + expect(ganttTrace.startTimeUnixMs).toEqual(1729001599633.602); + expect(ganttTrace.endTimeUnixMs).toEqual(1729001599964.748); + }); +}); diff --git a/TracingGanttChart/src/TracingGanttChart/trace.ts b/TracingGanttChart/src/TracingGanttChart/trace.ts new file mode 100644 index 0000000..d03c412 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/trace.ts @@ -0,0 +1,52 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Span, Trace } from '@perses-dev/core'; + +/** holds the trace and computed properties required for the Gantt chart */ +export interface GanttTrace { + rootSpan: Span; + + // computed properties of the rootSpan + startTimeUnixMs: number; + endTimeUnixMs: number; +} + +/** this function precomputes common fields, for example the start and end time of a trace */ +export function getTraceModel(trace: Trace): GanttTrace { + const limits = { startTimeUnixMs: trace.rootSpan.startTimeUnixMs, endTimeUnixMs: trace.rootSpan.endTimeUnixMs }; + getStartAndEndTime(trace.rootSpan, limits); + + return { + rootSpan: trace.rootSpan, + startTimeUnixMs: limits.startTimeUnixMs, + endTimeUnixMs: limits.endTimeUnixMs, + }; +} + +/** + * Compute the start and end of a trace. + * In most cases (but not all) this is rootSpan.startTime / rootSpan.endTime. + */ +function getStartAndEndTime(span: Span, limits: { startTimeUnixMs: number; endTimeUnixMs: number }): void { + if (span.startTimeUnixMs < limits.startTimeUnixMs) { + limits.startTimeUnixMs = span.startTimeUnixMs; + } + if (span.endTimeUnixMs > limits.endTimeUnixMs) { + limits.endTimeUnixMs = span.endTimeUnixMs; + } + + for (const child of span.childSpans) { + getStartAndEndTime(child, limits); + } +} diff --git a/TracingGanttChart/src/TracingGanttChart/utils.test.ts b/TracingGanttChart/src/TracingGanttChart/utils.test.ts new file mode 100644 index 0000000..ab7dad3 --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/utils.test.ts @@ -0,0 +1,29 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { formatDuration } from './utils'; + +describe('utils', () => { + it.each([ + [0.1, '100μs'], + [100, '100ms'], + [100.5, '100.5ms'], + [100.55, '100.55ms'], + [1000, '1s'], + [1500, '1.5s'], + [1550, '1.55s'], + [1555, '1.55s'], + ])('formatDuration(%f)', (x, expected) => { + expect(formatDuration(x)).toEqual(expected); + }); +}); diff --git a/TracingGanttChart/src/TracingGanttChart/utils.ts b/TracingGanttChart/src/TracingGanttChart/utils.ts new file mode 100644 index 0000000..43f933c --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChart/utils.ts @@ -0,0 +1,69 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Span, SpanStatusError } from '@perses-dev/core'; +import { PersesChartsTheme } from '@perses-dev/components'; +import { Theme } from '@mui/material'; +import { getConsistentCategoricalColor, getConsistentColor } from './palette'; + +/** + * Viewport contains the current zoom, i.e. which timeframe of the trace should be visible + */ +export interface Viewport { + startTimeUnixMs: number; + endTimeUnixMs: number; +} + +/** minimum span width, i.e. increase width if the calculated width is too small to be visible */ +export const minSpanWidthPx = 2; +export const rowHeight = '2rem'; +export const spanHasError = (span: Span): boolean => span.status?.code === SpanStatusError; + +export function getServiceColor( + muiTheme: Theme, + chartsTheme: PersesChartsTheme, + paletteMode: 'auto' | 'categorical' | undefined, + serviceName: string, + error = false +): string { + switch (paletteMode) { + case 'categorical': { + // ECharts type for color is not always an array but it is always an array in ChartsProvider + const categoricalPalette = chartsTheme.echartsTheme.color as string[]; + const errorPalette = [muiTheme.palette.error.light, muiTheme.palette.error.main, muiTheme.palette.error.dark]; + return getConsistentCategoricalColor(serviceName, error, categoricalPalette, errorPalette); + } + + default: + return getConsistentColor(serviceName, error); + } +} + +export function getSpanColor( + muiTheme: Theme, + chartsTheme: PersesChartsTheme, + paletteMode: 'auto' | 'categorical' | undefined, + span: Span +): string { + return getServiceColor(muiTheme, chartsTheme, paletteMode, span.resource.serviceName, spanHasError(span)); +} + +export function formatDuration(timeMs: number): string { + if (timeMs < 1) { + return `${Math.round(timeMs * 1000)}μs`; + } + if (timeMs < 1000) { + return `${+timeMs.toFixed(2)}ms`; + } + return `${+(timeMs / 1000).toFixed(2)}s`; +} diff --git a/TracingGanttChart/src/TracingGanttChartPanel.tsx b/TracingGanttChart/src/TracingGanttChartPanel.tsx new file mode 100644 index 0000000..b9cc94f --- /dev/null +++ b/TracingGanttChart/src/TracingGanttChartPanel.tsx @@ -0,0 +1,62 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PanelProps, useDataQueries } from '@perses-dev/plugin-system'; +import { LoadingOverlay, NoDataOverlay, TextOverlay, useChartsTheme } from '@perses-dev/components'; +import { Box } from '@mui/material'; +import { ReactElement } from 'react'; +import { TracingGanttChartOptions } from './gantt-chart-model'; +import { TracingGanttChart } from './TracingGanttChart/TracingGanttChart'; +import { AttributeLinks } from './TracingGanttChart/DetailPane/Attributes'; + +export interface TracingGanttChartPanelProps extends PanelProps { + /** + * Allows custom links for each attribute in the detail pane. + * Example: + * { + * 'k8s.pod.name': (attrs) => `/my/console/namespace/${attrs['k8s.namespace.name']?.stringValue}/${attrs['k8s.pod.name']?.stringValue}/detail` + * } + */ + attributeLinks?: AttributeLinks; +} + +export function TracingGanttChartPanel(props: TracingGanttChartPanelProps): ReactElement { + const { spec, attributeLinks } = props; + const chartsTheme = useChartsTheme(); + const contentPadding = chartsTheme.container.padding.default; + const { isFetching, isLoading, queryResults } = useDataQueries('TraceQuery'); + + if (queryResults.length > 1) { + return ; + } + + if (isLoading || isFetching) { + return ; + } + + const queryError = queryResults.find((d) => d.error); + if (queryError) { + throw queryError.error; + } + + const trace = queryResults[0]?.data?.trace; + if (!trace) { + return ; + } + + return ( + + + + ); +} diff --git a/TracingGanttChart/src/bootstrap.tsx b/TracingGanttChart/src/bootstrap.tsx new file mode 100644 index 0000000..3380d4a --- /dev/null +++ b/TracingGanttChart/src/bootstrap.tsx @@ -0,0 +1,18 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +const root = ReactDOM.createRoot(document.getElementById('root')!); +root.render(); diff --git a/TracingGanttChart/src/env.d.ts b/TracingGanttChart/src/env.d.ts new file mode 100644 index 0000000..b302dd9 --- /dev/null +++ b/TracingGanttChart/src/env.d.ts @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// diff --git a/TracingGanttChart/src/gantt-chart-model.ts b/TracingGanttChart/src/gantt-chart-model.ts new file mode 100644 index 0000000..f19b81a --- /dev/null +++ b/TracingGanttChart/src/gantt-chart-model.ts @@ -0,0 +1,35 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * The Options object type supported by the TracingGanttChart panel plugin. + */ +// Note: The interface attributes must match cue/schemas/panels/tracing-gantt-chart/tracing-gantt-chart.cue +export interface TracingGanttChartOptions { + visual?: TracingGanttChartVisualOptions; +} + +export interface TracingGanttChartVisualOptions { + palette?: TracingGanttChartPaletteOptions; +} + +export interface TracingGanttChartPaletteOptions { + mode: 'auto' | 'categorical'; +} + +/** + * Creates the initial/empty options for a TracingGanttChart panel. + */ +export function createInitialTracingGanttChartOptions(): Record { + return {}; +} diff --git a/TracingGanttChart/src/index.tsx b/TracingGanttChart/src/index.tsx new file mode 100644 index 0000000..d3d1d92 --- /dev/null +++ b/TracingGanttChart/src/index.tsx @@ -0,0 +1,14 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import('./bootstrap'); diff --git a/TracingGanttChart/src/setup-tests.ts b/TracingGanttChart/src/setup-tests.ts new file mode 100644 index 0000000..daa0e5a --- /dev/null +++ b/TracingGanttChart/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts/core'); diff --git a/TracingGanttChart/src/test/get-trace-data-jaeger.ts b/TracingGanttChart/src/test/get-trace-data-jaeger.ts new file mode 100644 index 0000000..5933190 --- /dev/null +++ b/TracingGanttChart/src/test/get-trace-data-jaeger.ts @@ -0,0 +1,157 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Span, SpanEvent, Trace, TraceAttribute, TraceAttributeValue, TraceResource } from '@perses-dev/core'; + +// the following jaeger data types and parsing function should eventually be moved to a jaeger plugin + +export interface JaegerTrace { + traceID: string; + spans: JaegerSpan[]; + processes: unknown; + warnings: unknown; +} + +export interface JaegerSpan { + traceID: string; + spanID: string; + hasChildren: boolean; + childSpanIds: string[]; + depth: number; + processID: string; + process: JaegerProcess; + + operationName: string; + /** start time in microseconds */ + startTime: number; + relativeStartTime: number; + duration: number; + tags: JaegerTag[]; + references: unknown; + logs: unknown; + warnings: unknown; +} + +interface JaegerProcess { + serviceName: string; + tags: JaegerTag[]; +} + +type JaegerTag = + | { + type: 'string'; + key: string; + value: string; + } + | { + type: 'int64'; + key: string; + value: number; + }; + +function parseTag(tags: JaegerTag): TraceAttribute { + let value: TraceAttributeValue; + switch (tags.type) { + case 'string': + value = { stringValue: tags.value }; + break; + case 'int64': + value = { intValue: tags.value.toString() }; + break; + default: + // eslint-disable-next-line @typescript-eslint/no-explicit-any + throw new Error(`unknown jaeger tag type ${(tags as any).type}`); + } + return { key: tags.key, value }; +} + +function parseProcess(process: JaegerProcess): TraceResource { + return { + serviceName: process.serviceName, + attributes: process.tags.map(parseTag), + }; +} + +function parseSpan(span: JaegerSpan): { + traceId: string; + spanId: string; + name: string; + kind: string; + startTimeUnixMs: number; + endTimeUnixMs: number; + attributes: TraceAttribute[]; + events: SpanEvent[]; + status: Record; +} { + return { + traceId: span.traceID, + spanId: span.spanID, + name: span.operationName, + kind: '', + startTimeUnixMs: span.startTime / 1000, + endTimeUnixMs: (span.startTime + span.duration) / 1000, + attributes: span.tags.map(parseTag), + events: [], + status: {}, + }; +} + +export function parseJaegerTrace(jaegerTrace: JaegerTrace): Trace { + // first pass: build lookup table + const lookup = new Map(); + for (const jaegerSpan of jaegerTrace.spans) { + const span: Span = { + resource: parseProcess(jaegerSpan.process), + scope: { + name: jaegerSpan.processID, + }, + childSpans: [], + ...parseSpan(jaegerSpan), + }; + lookup.set(jaegerSpan.spanID, span); + } + + // second pass: build tree based on childSpanIds property + let rootSpan: Span | null = null; + for (const jaegerSpan of jaegerTrace.spans) { + const span = lookup.get(jaegerSpan.spanID); + if (!span) { + continue; + } + + if (jaegerSpan.depth === 0) { + rootSpan = span; + } + + const childSpans: Span[] = []; + for (const childSpanId of jaegerSpan.childSpanIds) { + const childSpan = lookup.get(childSpanId); + if (!childSpan) { + continue; + } + + childSpan.parentSpan = span; + childSpan.parentSpanId = span.spanId; + childSpans.push(childSpan); + } + span.childSpans = childSpans.sort((a, b) => a.startTimeUnixMs - b.startTimeUnixMs); + } + + if (!rootSpan) { + throw new Error('root span not found'); + } + + return { + rootSpan, + }; +} diff --git a/TracingGanttChart/src/test/mock-trace-data.ts b/TracingGanttChart/src/test/mock-trace-data.ts new file mode 100644 index 0000000..af0b0d4 --- /dev/null +++ b/TracingGanttChart/src/test/mock-trace-data.ts @@ -0,0 +1,885 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Span, Trace, TraceData } from '@perses-dev/core'; +import { GanttTrace } from '../TracingGanttChart/trace'; +import { JaegerTrace, parseJaegerTrace } from './get-trace-data-jaeger'; + +function addParentReferences(span: Span): void { + for (const child of span.childSpans) { + child.parentSpan = span; + addParentReferences(child); + } +} + +/** + * Mock data we get from getTraceData() in @perses/tempo-plugin. + */ +export const MOCK_TRACE_SEARCH_RESULT: TraceData = { + searchResult: [ + { + startTimeUnixMs: 1702915645000, // unix epoch time in milliseconds + durationMs: 100, + serviceStats: { + 'service-name': { + spanCount: 10, + }, + 'second-service-name': { + spanCount: 3, + errorCount: 2, + }, + }, + traceId: '123', + rootServiceName: 'service-name', + rootTraceName: 'span-name', + }, + ], + metadata: { + executedQueryString: '{duration > 500ms}', + }, +}; + +export const MOCK_TRACE_SEARCH_RESULT_EMPTY: TraceData = { + searchResult: [], + metadata: { + executedQueryString: '{duration > 500ms}', + }, +}; + +/** + * Mocks results obtained from useTraceQueries() in @perses/plugin-system/runtime. + * This function uses then React TanStack function useQueries(fooQuery) to + * handle fetching. + */ +export const MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: MOCK_TRACE_SEARCH_RESULT, + dataUpdatedAt: 1666500979895, + definition: { + kind: 'TraceQuery', + spec: { + plugin: { + kind: 'TempoTraceQuery', + spec: { + query: '{}', + datasource: { + kind: 'TempoDatasource', + name: 'tempolocal', + }, + }, + }, + }, + }, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TRACE_SEARCH_RESULT_QUERY_RESULT_EMPTY = [ + { + status: 'success', + fetchStatus: 'idle', + isLoading: false, + isSuccess: true, + isError: false, + data: MOCK_TRACE_SEARCH_RESULT_EMPTY, + dataUpdatedAt: 1666500979895, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPaused: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, + }, +]; + +export const MOCK_TRACE: Trace = { + rootSpan: { + resource: { + serviceName: 'shop-backend', + attributes: [{ key: 'service.name', value: { stringValue: 'shop-backend' } }], + }, + scope: { name: 'k6' }, + traceId: 'tid1', + spanId: 'sid1', + name: 'testRootSpan', + kind: 'SPAN_KIND_SERVER', + startTimeUnixMs: 1000, + endTimeUnixMs: 2000, + attributes: [], + events: [], + childSpans: [ + { + resource: { + serviceName: 'shop-backend', + attributes: [{ key: 'service.name', value: { stringValue: 'shop-backend' } }], + }, + scope: { name: 'k6' }, + childSpans: [ + { + resource: { + serviceName: 'shop-backend', + attributes: [{ key: 'service.name', value: { stringValue: 'shop-backend' } }], + }, + scope: { name: 'k6' }, + childSpans: [], + traceId: 'tid1', + spanId: 'sid3', + parentSpanId: 'sid2', + name: 'testChildSpan3', + kind: 'SPAN_KIND_CLIENT', + startTimeUnixMs: 1300, + endTimeUnixMs: 1450, + attributes: [{ key: 'http.method', value: { stringValue: 'PUT' } }], + events: [], + }, + ], + traceId: 'tid1', + spanId: 'sid2', + parentSpanId: 'sid1', + name: 'testChildSpan2', + kind: 'SPAN_KIND_CLIENT', + startTimeUnixMs: 1100, + endTimeUnixMs: 1200, + attributes: [{ key: 'http.method', value: { stringValue: 'DELETE' } }], + events: [ + { + timeUnixMs: 1150, + name: 'event1_name', + attributes: [{ key: 'event1_key', value: { stringValue: 'event1_value' } }], + }, + ], + status: { message: 'Forbidden', code: 'STATUS_CODE_ERROR' }, + }, + ], + }, +}; +addParentReferences(MOCK_TRACE.rootSpan); + +export const MOCK_GANTT_TRACE: GanttTrace = { + rootSpan: MOCK_TRACE.rootSpan, + startTimeUnixMs: MOCK_TRACE.rootSpan.startTimeUnixMs, + endTimeUnixMs: MOCK_TRACE.rootSpan.endTimeUnixMs, +}; + +const MOCK_JAEGER_TRACE_ASYNC: JaegerTrace = { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spans: [ + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '5b6c5367c8f55a07', + operationName: 'post1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599633651, + duration: 3081, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'POST', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path/id-24', + }, + ], + logs: [ + { + timestamp: 1729001599633766, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599636650, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599636673, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 49, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '0bd40fdf749d38af', + operationName: 'get1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599733700, + duration: 2719, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'GET', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path?id=id-24', + }, + ], + logs: [ + { + timestamp: 1729001599733877, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599736343, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599736364, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 100098, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '8a83db29894c10c4', + operationName: 'put1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599833654, + duration: 2803, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'PUT', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path/id-24', + }, + ], + logs: [ + { + timestamp: 1729001599833794, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599836396, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599836408, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 200052, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: '4653459b582b47cb', + operationName: 'delete1', + references: [ + { + refType: 'CHILD_OF', + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + span: { + traceID: '7d73f3ae841bf59a74cf5b52a328cfca', + spanID: 'b93f60c81f2a4ce0', + operationName: 'script', + references: [], + startTime: 1729001599633602, + duration: 3, + tags: [ + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + ], + logs: [], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 0, + depth: 0, + hasChildren: true, + childSpanIds: ['4653459b582b47cb', '8a83db29894c10c4', '0bd40fdf749d38af', '5b6c5367c8f55a07'], + }, + }, + ], + startTime: 1729001599944592, + duration: 20156, + tags: [ + { + key: 'http.request.method', + type: 'string', + value: 'DELETE', + }, + { + key: 'http.response.status_code', + type: 'int64', + value: 200, + }, + { + key: 'otel.status_code', + type: 'string', + value: 'OK', + }, + { + key: 'span.kind', + type: 'string', + value: 'client', + }, + { + key: 'url.full', + type: 'string', + value: 'http://server-mock:8080/url/example/path/id-24', + }, + ], + logs: [ + { + timestamp: 1729001599944786, + fields: [ + { + key: 'event', + type: 'string', + value: 'Request sent', + }, + ], + }, + { + timestamp: 1729001599964619, + fields: [ + { + key: 'event', + type: 'string', + value: 'Response received', + }, + ], + }, + { + timestamp: 1729001599964650, + fields: [ + { + key: 'event', + type: 'string', + value: 'Body received', + }, + ], + }, + ], + processID: 'p1', + warnings: [], + process: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + relativeStartTime: 310990, + depth: 1, + hasChildren: false, + childSpanIds: [], + }, + ], + processes: { + p1: { + serviceName: 'hermes', + tags: [ + { + key: 'otel.library.name', + type: 'string', + value: 'hermes_client', + }, + { + key: 'telemetry.sdk.language', + type: 'string', + value: 'cpp', + }, + { + key: 'telemetry.sdk.name', + type: 'string', + value: 'opentelemetry', + }, + { + key: 'telemetry.sdk.version', + type: 'string', + value: '1.12.0', + }, + ], + }, + }, + warnings: null, +}; + +export const MOCK_TRACE_ASYNC = parseJaegerTrace(MOCK_JAEGER_TRACE_ASYNC); diff --git a/TracingGanttChart/tsconfig.json b/TracingGanttChart/tsconfig.json new file mode 100644 index 0000000..e6b9bdf --- /dev/null +++ b/TracingGanttChart/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["DOM", "ES2020"], + "module": "ESNext", + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true, + "isolatedModules": true, + "resolveJsonModule": true, + "moduleResolution": "bundler", + "useDefineForClassFields": true + }, + "include": ["src"] +} diff --git a/jest.shared.ts b/jest.shared.ts new file mode 100644 index 0000000..c43270a --- /dev/null +++ b/jest.shared.ts @@ -0,0 +1,42 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { readFileSync } from 'fs'; +import { resolve } from 'path'; +import type { Config } from '@jest/types'; + +const swcrcPath = resolve(__dirname, './.cjs.swcrc'); +const swcrc = JSON.parse(readFileSync(swcrcPath, 'utf-8')); + +// Common Jest configuration shared across packages +const config: Config.InitialOptions = { + testEnvironment: 'jsdom', + roots: ['/src'], + moduleNameMapper: { + '^echarts/(.*)$': 'echarts', + + // Use polyfill for jsdom environment + '^use-resize-observer$': 'use-resize-observer/polyfilled', + + // Configure Jest to handle stylesheets + '\\.(css|less)$': '/../stylesMock.js', + }, + transform: { + // This does not do type-checking and assumes that's happening elsewhere for TS test files (e.g. as part of the + // build process) + // exclude: [] + swcrc: false => https://github.com/swc-project/jest/issues/62 + '^.+\\.(ts|tsx|js|jsx)$': ['@swc/jest', { ...swcrc, exclude: [], swcrc: false }], + }, +}; + +export default config; diff --git a/package-lock.json b/package-lock.json index 3f08f13..0b625f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,18 +11,34 @@ "BarChart", "GaugeChart", "MarkdownChart", + "PieChart", + "Prometheus", + "ScatterChart", "StatChart", + "StaticListVariable", + "StatusHistoryChart", + "Table", "TimeSeriesChart", - "Prometheus", - "StaticListVariable" + "TimeSeriesTable", + "TraceTable", + "TracingGanttChart" ], "devDependencies": { "@rsbuild/core": "^1.1.10", "@rsbuild/plugin-react": "^1.1.0", + "@rspack/core": "^0.6.5", + "@swc/core": "^1.7.10", + "@swc/jest": "^0.2.37", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.1.0", + "@testing-library/user-event": "^13.5.0", "@types/color-hash": "^2.0.0", + "@types/jest": "^29.5.14", "@types/lodash": "^4.17.5", "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.8.0", + "cross-env": "^7.0.3", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", @@ -30,12 +46,15 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.2", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "ts-node": "^10.9.2", "typescript": "^5.4.2" } }, "BarChart": { "name": "@perses-dev/bar-chart", - "version": "0.3.1", + "version": "0.4.0", "dependencies": { "@module-federation/enhanced": "^0.1.11" }, @@ -49,6 +68,7 @@ "date-fns": "^2.29.3", "date-fns-tz": "^1.3.8", "echarts": "5.5.0", + "immer": "^9.0.15", "lodash": "^4.17.21", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0", @@ -57,7 +77,7 @@ }, "GaugeChart": { "name": "@perses-dev/gauge-chart", - "version": "0.3.0", + "version": "0.4.0", "dependencies": { "@module-federation/enhanced": "^0.1.11" }, @@ -71,6 +91,7 @@ "date-fns": "^2.29.3", "date-fns-tz": "^1.3.8", "echarts": "5.5.0", + "immer": "^9.0.15", "lodash": "^4.17.21", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0", @@ -79,11 +100,11 @@ }, "MarkdownChart": { "name": "@perses-dev/markdown-chart", - "version": "0.3.0", + "version": "0.4.0", "dependencies": { "@module-federation/enhanced": "^0.1.11", - "dompurify": "^2.4.7", - "marked": "^4.3.0" + "dompurify": "^3.2.3", + "marked": "^15.0.6" }, "peerDependencies": { "@emotion/react": "^11.7.1", @@ -101,12 +122,48 @@ "use-resize-observer": "^9.0.0" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.1.tgz", + "integrity": "sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==", + "dev": true + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@atlaskit/pragmatic-drag-and-drop": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@atlaskit/pragmatic-drag-and-drop/-/pragmatic-drag-and-drop-1.4.0.tgz", + "integrity": "sha512-qRY3PTJIcxfl/QB8Gwswz+BRvlmgAC5pB+J2hL6dkIxgqAgVwOhAamMUKsrOcFU/axG2Q7RbNs1xfoLKDuhoPg==", + "dependencies": { + "@babel/runtime": "^7.0.0", + "bind-event-listener": "^3.0.0", + "raf-schd": "^4.0.3" + } + }, + "node_modules/@atlaskit/pragmatic-drag-and-drop-hitbox": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@atlaskit/pragmatic-drag-and-drop-hitbox/-/pragmatic-drag-and-drop-hitbox-1.0.3.tgz", + "integrity": "sha512-/Sbu/HqN2VGLYBhnsG7SbRNg98XKkbF6L7XDdBi+izRybfaK1FeMfodPpm/xnBHPJzwYMdkE0qtLyv6afhgMUA==", + "dependencies": { + "@atlaskit/pragmatic-drag-and-drop": "^1.1.0", + "@babel/runtime": "^7.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -116,15 +173,67 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/compat-data": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "license": "MIT", - "peer": true, + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -133,12 +242,50 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, "node_modules/@babel/helper-module-imports": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "license": "MIT", - "peer": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -147,12 +294,36 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -161,20 +332,38 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "license": "MIT", - "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", - "license": "MIT", - "peer": true, + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", + "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -183,12 +372,232 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/runtime": { "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "peer": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -200,8 +609,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", - "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.25.9", "@babel/parser": "^7.25.9", @@ -212,17 +619,15 @@ } }, "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "license": "MIT", - "peer": true, + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", + "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.5", "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", + "@babel/types": "^7.26.5", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -230,22 +635,10 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "license": "MIT", - "peer": true, + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", + "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -254,31 +647,27 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "node_modules/@codemirror/autocomplete": { - "version": "6.18.3", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.3.tgz", - "integrity": "sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==", - "license": "MIT", - "peer": true, + "version": "6.18.4", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.4.tgz", + "integrity": "sha512-sFAphGQIqyQZfP2ZBsSHV7xQvo9Py0rV0dW7W3IMRdS+zDuNb2l3no78CvUaWKGfzFjI4FTrLdUSj86IGb2hRA==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" - }, - "peerDependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" } }, "node_modules/@codemirror/commands": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.7.1.tgz", - "integrity": "sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==", - "license": "MIT", - "peer": true, + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.0.tgz", + "integrity": "sha512-q8VPEFaEP4ikSlt6ZxjB3zW72+7osfAYW9i8Zu943uqbKuz6utc1+F170hyLUCUltXORjQXRyYQNfkckzA/bPQ==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", @@ -290,19 +679,15 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", - "license": "MIT", - "peer": true, "dependencies": { "@codemirror/language": "^6.0.0", "@lezer/json": "^1.0.0" } }, "node_modules/@codemirror/language": { - "version": "6.10.7", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.7.tgz", - "integrity": "sha512-aOswhVOLYhMNeqykt4P7+ukQSpGL0ynZYaEyFDVHE7fl2xgluU3yuE9MdgYNfw6EmaNidoFMIQ2iTh1ADrnT6A==", - "license": "MIT", - "peer": true, + "version": "6.10.8", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.8.tgz", + "integrity": "sha512-wcP8XPPhDH2vTqf181U8MbZnW+tDyPYy0UzVOa+oHORjyT+mhhom9vBd7dApJwoDz9Nb/a8kHjJIsuA/t8vNFw==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -316,8 +701,6 @@ "version": "6.8.4", "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.4.tgz", "integrity": "sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A==", - "license": "MIT", - "peer": true, "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.35.0", @@ -328,8 +711,6 @@ "version": "6.5.8", "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.8.tgz", "integrity": "sha512-PoWtZvo7c1XFeZWmmyaOp2G0XVbOnm+fJzvghqGAktBW3cufwJUWvSCcNG0ppXiBEM05mZu6RhMtXPv2hpllig==", - "license": "MIT", - "peer": true, "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", @@ -337,11 +718,9 @@ } }, "node_modules/@codemirror/state": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz", - "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==", - "license": "MIT", - "peer": true, + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.1.tgz", + "integrity": "sha512-3rA9lcwciEB47ZevqvD8qgbzhM9qMb8vCcQCNmDfVRPQG4JT9mSb0Jg8H7YjKGGQcFnLN323fj9jdnG59Kx6bg==", "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } @@ -350,8 +729,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz", "integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==", - "license": "MIT", - "peer": true, "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -360,22 +737,41 @@ } }, "node_modules/@codemirror/view": { - "version": "6.35.3", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.35.3.tgz", - "integrity": "sha512-ScY7L8+EGdPl4QtoBiOzE4FELp7JmNUsBvgBcCakXWM2uiv/K89VAzU3BMDscf0DsACLvTKePbd5+cFDTcei6g==", - "license": "MIT", - "peer": true, + "version": "6.36.2", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.2.tgz", + "integrity": "sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==", "dependencies": { "@codemirror/state": "^6.5.0", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", - "license": "MIT", "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.16.7", @@ -395,7 +791,6 @@ "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", - "license": "MIT", "peer": true, "dependencies": { "@emotion/memoize": "^0.9.0", @@ -409,14 +804,12 @@ "version": "0.9.2", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", - "license": "MIT", "peer": true }, "node_modules/@emotion/is-prop-valid": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", - "license": "MIT", "peer": true, "dependencies": { "@emotion/memoize": "^0.9.0" @@ -426,14 +819,12 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", - "license": "MIT", "peer": true }, "node_modules/@emotion/react": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", - "license": "MIT", "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", @@ -458,7 +849,6 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", - "license": "MIT", "peer": true, "dependencies": { "@emotion/hash": "^0.9.2", @@ -472,14 +862,12 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", - "license": "MIT", "peer": true }, "node_modules/@emotion/styled": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", - "license": "MIT", "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", @@ -503,14 +891,12 @@ "version": "0.10.0", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", - "license": "MIT", "peer": true }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", - "license": "MIT", "peer": true, "peerDependencies": { "react": ">=16.8.0" @@ -520,14 +906,12 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", - "license": "MIT", "peer": true }, "node_modules/@emotion/weak-memoize": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", - "license": "MIT", "peer": true }, "node_modules/@eslint-community/eslint-utils": { @@ -535,7 +919,6 @@ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, - "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -554,7 +937,6 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -564,7 +946,6 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -588,18 +969,31 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -607,42 +1001,50 @@ "node": "*" } }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@floating-ui/core": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", - "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", - "license": "MIT", + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", + "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", "peer": true, "dependencies": { - "@floating-ui/utils": "^0.2.8" + "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.12", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz", - "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==", - "license": "MIT", + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", "peer": true, "dependencies": { "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.8" + "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/react-dom": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", - "license": "MIT", "peer": true, "dependencies": { "@floating-ui/dom": "^1.0.0" @@ -653,24 +1055,20 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", - "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", - "license": "MIT", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "peer": true }, "node_modules/@fontsource/lato": { "version": "4.5.10", "resolved": "https://registry.npmjs.org/@fontsource/lato/-/lato-4.5.10.tgz", - "integrity": "sha512-2hYR6r661Cq9B8zugtu6yxuOKqrVhAgfOSaPSq8XoxbC4ebsl0KOTy/vPoP+9U7JuQVLfrmikirW4a9Z0nDUug==", - "license": "MIT", - "peer": true + "integrity": "sha512-2hYR6r661Cq9B8zugtu6yxuOKqrVhAgfOSaPSq8XoxbC4ebsl0KOTy/vPoP+9U7JuQVLfrmikirW4a9Z0nDUug==" }, "node_modules/@hookform/resolvers": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.1.tgz", - "integrity": "sha512-ud2HqmGBM0P0IABqoskKWI6PEf6ZDDBZkFqe2Vnl+mTHCEHzr3ISjjZyCwTjC/qpL25JC9aIDkloQejvMeq0ug==", - "license": "MIT", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", + "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==", "peer": true, "peerDependencies": { "react-hook-form": "^7.0.0" @@ -682,7 +1080,6 @@ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, - "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -697,7 +1094,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -708,7 +1104,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -721,7 +1116,6 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -735,544 +1129,761 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause" + "dev": true }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", - "peer": true, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.0.0" + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "peer": true, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "peer": true, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@juggle/resize-observer": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", - "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", - "license": "Apache-2.0", - "peer": true - }, - "node_modules/@lezer/common": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", - "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", - "license": "MIT", - "peer": true - }, - "node_modules/@lezer/highlight": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", - "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", - "license": "MIT", - "peer": true, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "dependencies": { - "@lezer/common": "^1.0.0" + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@lezer/json": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.2.tgz", - "integrity": "sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==", - "license": "MIT", - "peer": true, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@lezer/lr": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", - "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", - "license": "MIT", - "peer": true, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "dependencies": { - "@lezer/common": "^1.0.0" + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@marijn/find-cluster-break": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", - "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", - "license": "MIT", - "peer": true + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/@module-federation/dts-plugin": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.1.21.tgz", - "integrity": "sha512-o0floS9ZN6i9C3Jmx0O65LasJga03ejxtzjIALzJUJR6fKykOP/Bm39MaBNk/MHoFrgnx0q6SVmARBjINcvPjA==", - "license": "MIT", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, "dependencies": { - "@module-federation/managers": "0.1.21", - "@module-federation/sdk": "0.1.21", - "@module-federation/third-party-dts-extractor": "0.1.21", - "adm-zip": "^0.5.10", - "ansi-colors": "^4.1.3", - "axios": "^1.6.7", - "chalk": "3.0.0", - "fs-extra": "9.1.0", - "isomorphic-ws": "5.0.0", - "koa": "2.11.0", - "lodash.clonedeepwith": "4.5.0", - "log4js": "6.9.1", - "node-schedule": "2.1.1", - "rambda": "^9.1.0", - "ws": "8.17.0" - }, - "peerDependencies": { - "typescript": "^4.9.0 || ^5.0.0", - "vue-tsc": "^1.0.24" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" }, - "peerDependenciesMeta": { - "vue-tsc": { - "optional": true - } + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@module-federation/dts-plugin/node_modules/@module-federation/sdk": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.1.21.tgz", - "integrity": "sha512-r7xPiAm+O4e+8Zvw+8b4ToeD0D0VJD004nHmt+Y8r/l98J2eA6di72Vn1FeyjtQbCrFtiMw3ts/dlqtcmIBipw==", - "license": "MIT" - }, - "node_modules/@module-federation/dts-plugin/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "license": "MIT", + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@module-federation/enhanced": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.1.21.tgz", - "integrity": "sha512-FUrwcxj6qdyDG6RhaUR1vhO4aJq8/ZtvWxi4Hf9QTE7yLsBY87cCV0FD0bsYLN5femuik53Q7f3Zw/WxQ8u6Qg==", - "license": "MIT", + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, "dependencies": { - "@module-federation/dts-plugin": "0.1.21", - "@module-federation/managers": "0.1.21", - "@module-federation/manifest": "0.1.21", - "@module-federation/rspack": "0.1.21", - "@module-federation/runtime-tools": "0.1.21", - "@module-federation/sdk": "0.1.21", - "upath": "2.0.1" + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { - "typescript": "^4.9.0 || ^5.0.0", - "vue-tsc": "^1.0.24", - "webpack": "^5.0.0" + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "vue-tsc": { - "optional": true - }, - "webpack": { + "node-notifier": { "optional": true } } }, - "node_modules/@module-federation/enhanced/node_modules/@module-federation/runtime": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.1.21.tgz", - "integrity": "sha512-/p4BhZ0SnjJuiL0wwu+FebFgIUJ9vM+oCY7CyprUHImyi/Y23ulI61WNWMVrKQGgdMoXQDQCL8RH4EnrVP2ZFw==", - "license": "MIT", + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { - "@module-federation/sdk": "0.1.21" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@module-federation/enhanced/node_modules/@module-federation/runtime-tools": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.1.21.tgz", - "integrity": "sha512-anayDx/wiL80XhD+CPTVvpYvKc4CNbEIy9fGoOCbMQKaKZhgDDE1461fy5abpgJphtj9NoUgCIpEn/O5fHNIfA==", - "license": "MIT", + "node_modules/@jest/core/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, "dependencies": { - "@module-federation/runtime": "0.1.21", - "@module-federation/webpack-bundler-runtime": "0.1.21" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@module-federation/enhanced/node_modules/@module-federation/sdk": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.1.21.tgz", - "integrity": "sha512-r7xPiAm+O4e+8Zvw+8b4ToeD0D0VJD004nHmt+Y8r/l98J2eA6di72Vn1FeyjtQbCrFtiMw3ts/dlqtcmIBipw==", - "license": "MIT" - }, - "node_modules/@module-federation/enhanced/node_modules/@module-federation/webpack-bundler-runtime": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.1.21.tgz", - "integrity": "sha512-WJg133fvC8PMm3RRCul9+bzaMgyTYD7QpseDBscIWyKdZB80wMBTXuRKbKMwujLXHEPCJX+rkgGwolECnSuCiA==", - "license": "MIT", - "dependencies": { - "@module-federation/runtime": "0.1.21", - "@module-federation/sdk": "0.1.21" + "node_modules/@jest/core/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@module-federation/managers": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.1.21.tgz", - "integrity": "sha512-rmN379q8SIovzZBK3s9z1KE84wXgeJR83cFdHJo/1xDAiou7WneW/fyefIvMsGN61hfFxygHoK0GcgXinFq9GA==", - "license": "MIT", + "node_modules/@jest/core/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/@jest/create-cache-key-function": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "dev": true, "dependencies": { - "@module-federation/sdk": "0.1.21", - "find-pkg": "2.0.0", - "fs-extra": "9.1.0" + "@jest/types": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@module-federation/managers/node_modules/@module-federation/sdk": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.1.21.tgz", - "integrity": "sha512-r7xPiAm+O4e+8Zvw+8b4ToeD0D0VJD004nHmt+Y8r/l98J2eA6di72Vn1FeyjtQbCrFtiMw3ts/dlqtcmIBipw==", - "license": "MIT" - }, - "node_modules/@module-federation/manifest": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.1.21.tgz", - "integrity": "sha512-L4iz5Y3qKJzz0bEAG6bDpPy+hHToHyW5xk8XhLJWFGVuXHjEHBl+cvE8tUAZkuWrhHrEmZBGHeHFxlDvdCKMjg==", - "license": "MIT", + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, "dependencies": { - "@module-federation/dts-plugin": "0.1.21", - "@module-federation/managers": "0.1.21", - "@module-federation/sdk": "0.1.21", - "chalk": "3.0.0", - "find-pkg": "2.0.0" + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@module-federation/manifest/node_modules/@module-federation/sdk": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.1.21.tgz", - "integrity": "sha512-r7xPiAm+O4e+8Zvw+8b4ToeD0D0VJD004nHmt+Y8r/l98J2eA6di72Vn1FeyjtQbCrFtiMw3ts/dlqtcmIBipw==", - "license": "MIT" - }, - "node_modules/@module-federation/manifest/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "license": "MIT", + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@module-federation/rspack": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.1.21.tgz", - "integrity": "sha512-30bmfb7tCQ8Uu4UKbapoSNNo+52a/rF3Nkje3/lv7xcmW/HGC2+/XJvckITo28jWsl5bzYe+pAu7XSExEAC0Ew==", - "license": "MIT", + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, "dependencies": { - "@module-federation/dts-plugin": "0.1.21", - "@module-federation/managers": "0.1.21", - "@module-federation/manifest": "0.1.21", - "@module-federation/runtime-tools": "0.1.21", - "@module-federation/sdk": "0.1.21" + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@module-federation/rspack/node_modules/@module-federation/runtime": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.1.21.tgz", - "integrity": "sha512-/p4BhZ0SnjJuiL0wwu+FebFgIUJ9vM+oCY7CyprUHImyi/Y23ulI61WNWMVrKQGgdMoXQDQCL8RH4EnrVP2ZFw==", - "license": "MIT", + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, "dependencies": { - "@module-federation/sdk": "0.1.21" + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@module-federation/rspack/node_modules/@module-federation/runtime-tools": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.1.21.tgz", - "integrity": "sha512-anayDx/wiL80XhD+CPTVvpYvKc4CNbEIy9fGoOCbMQKaKZhgDDE1461fy5abpgJphtj9NoUgCIpEn/O5fHNIfA==", - "license": "MIT", + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, "dependencies": { - "@module-federation/runtime": "0.1.21", - "@module-federation/webpack-bundler-runtime": "0.1.21" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@module-federation/rspack/node_modules/@module-federation/sdk": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.1.21.tgz", - "integrity": "sha512-r7xPiAm+O4e+8Zvw+8b4ToeD0D0VJD004nHmt+Y8r/l98J2eA6di72Vn1FeyjtQbCrFtiMw3ts/dlqtcmIBipw==", - "license": "MIT" - }, - "node_modules/@module-federation/rspack/node_modules/@module-federation/webpack-bundler-runtime": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.1.21.tgz", - "integrity": "sha512-WJg133fvC8PMm3RRCul9+bzaMgyTYD7QpseDBscIWyKdZB80wMBTXuRKbKMwujLXHEPCJX+rkgGwolECnSuCiA==", - "license": "MIT", + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, "dependencies": { - "@module-federation/runtime": "0.1.21", - "@module-federation/sdk": "0.1.21" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@module-federation/runtime": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.5.1.tgz", - "integrity": "sha512-xgiMUWwGLWDrvZc9JibuEbXIbhXg6z2oUkemogSvQ4LKvrl/n0kbqP1Blk669mXzyWbqtSp6PpvNdwaE1aN5xQ==", + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "@module-federation/sdk": "0.5.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@module-federation/runtime-tools": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.5.1.tgz", - "integrity": "sha512-nfBedkoZ3/SWyO0hnmaxuz0R0iGPSikHZOAZ0N/dVSQaIzlffUo35B5nlC2wgWIc0JdMZfkwkjZRrnuuDIJbzg==", + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, - "license": "MIT", "dependencies": { - "@module-federation/runtime": "0.5.1", - "@module-federation/webpack-bundler-runtime": "0.5.1" + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@module-federation/sdk": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.5.1.tgz", - "integrity": "sha512-exvchtjNURJJkpqjQ3/opdbfeT2wPKvrbnGnyRkrwW5o3FH1LaST1tkiNviT6OXTexGaVc2DahbdniQHVtQ7pA==", + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, - "license": "MIT" - }, - "node_modules/@module-federation/third-party-dts-extractor": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.1.21.tgz", - "integrity": "sha512-gf+8Ah+/3Fs7jgOycVYDBe6o/tDh/+k7WpaeaQGnRV2p7bkrqfZ07g3IoB/kza1VEJFH4SV3u3VmKD7eB5BjQw==", - "license": "MIT", "dependencies": { - "find-pkg": "2.0.0", - "fs-extra": "9.1.0", - "resolve": "1.22.8" + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@module-federation/third-party-dts-extractor/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "license": "MIT", + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@module-federation/webpack-bundler-runtime": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.5.1.tgz", - "integrity": "sha512-mMhRFH0k2VjwHt3Jol9JkUsmI/4XlrAoBG3E0o7HoyoPYv1UFOWyqAflfANcUPgbYpvqmyLzDcO+3IT36LXnrA==", + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, - "license": "MIT", "dependencies": { - "@module-federation/runtime": "0.5.1", - "@module-federation/sdk": "0.5.1" + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mui/base": { - "version": "5.0.0-beta.66", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.66.tgz", - "integrity": "sha512-1SzcNbtIms0o/Dx+599B6QbvR5qUMBUjwc2Gs47h1HsF7RcEFXxqaq7zrWkIWbvGctIIPx0j330oGx/SkF+UmA==", - "license": "MIT", - "peer": true, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.26.0", - "@floating-ui/react-dom": "^2.1.1", - "@mui/types": "^7.2.19", - "@mui/utils": "^6.2.0", - "@popperjs/core": "^2.11.8", - "clsx": "^2.1.1", - "prop-types": "^15.8.1" + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" }, "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mui/base/node_modules/@mui/utils": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.2.0.tgz", - "integrity": "sha512-77CaFJi+OIi2SjbPwCis8z5DXvE0dfx9hBz5FguZHt1VYFlWEPCWTHcMsQCahSErnfik5ebLsYK8+D+nsjGVfw==", - "license": "MIT", - "peer": true, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/types": "^7.2.19", - "@types/prop-types": "^15.7.14", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^19.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@mui/base/node_modules/react-is": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", - "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", - "license": "MIT", - "peer": true + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true }, - "node_modules/@mui/core-downloads-tracker": { - "version": "5.16.11", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.11.tgz", - "integrity": "sha512-2eVDGg9OvIXNRmfDUQyKYH+jNcjdv1JkCH5F2YDgUye5fMX5nxGiYHAUe1BXaXyDMaLSwXC7LRksEKMiIQsFdw==", - "license": "MIT", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mui/material": { - "version": "5.16.11", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.11.tgz", - "integrity": "sha512-uoc67oecKdnVKaMHBVE433YrMuxQs22xY5nIjRb5sAPB+GaeZQWp8brQ3/adeH6k2IDa8+9i2IVd4fNLuvHSvA==", - "license": "MIT", - "peer": true, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/core-downloads-tracker": "^5.16.11", - "@mui/system": "^5.16.8", - "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.8", - "@popperjs/core": "^2.11.8", - "@types/react-transition-group": "^4.4.10", - "clsx": "^2.1.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1", - "react-is": "^18.3.1", - "react-transition-group": "^4.4.5" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", + "peer": true + }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==" + }, + "node_modules/@module-federation/dts-plugin": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.1.21.tgz", + "integrity": "sha512-o0floS9ZN6i9C3Jmx0O65LasJga03ejxtzjIALzJUJR6fKykOP/Bm39MaBNk/MHoFrgnx0q6SVmARBjINcvPjA==", + "dependencies": { + "@module-federation/managers": "0.1.21", + "@module-federation/sdk": "0.1.21", + "@module-federation/third-party-dts-extractor": "0.1.21", + "adm-zip": "^0.5.10", + "ansi-colors": "^4.1.3", + "axios": "^1.6.7", + "chalk": "3.0.0", + "fs-extra": "9.1.0", + "isomorphic-ws": "5.0.0", + "koa": "2.11.0", + "lodash.clonedeepwith": "4.5.0", + "log4js": "6.9.1", + "node-schedule": "2.1.1", + "rambda": "^9.1.0", + "ws": "8.17.0" + }, + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": "^1.0.24" }, "peerDependenciesMeta": { - "@emotion/react": { + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/@module-federation/enhanced": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.1.21.tgz", + "integrity": "sha512-FUrwcxj6qdyDG6RhaUR1vhO4aJq8/ZtvWxi4Hf9QTE7yLsBY87cCV0FD0bsYLN5femuik53Q7f3Zw/WxQ8u6Qg==", + "dependencies": { + "@module-federation/dts-plugin": "0.1.21", + "@module-federation/managers": "0.1.21", + "@module-federation/manifest": "0.1.21", + "@module-federation/rspack": "0.1.21", + "@module-federation/runtime-tools": "0.1.21", + "@module-federation/sdk": "0.1.21", + "upath": "2.0.1" + }, + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": "^1.0.24", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { "optional": true }, - "@emotion/styled": { + "vue-tsc": { "optional": true }, - "@types/react": { + "webpack": { "optional": true } } }, - "node_modules/@mui/material/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT", - "peer": true + "node_modules/@module-federation/managers": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.1.21.tgz", + "integrity": "sha512-rmN379q8SIovzZBK3s9z1KE84wXgeJR83cFdHJo/1xDAiou7WneW/fyefIvMsGN61hfFxygHoK0GcgXinFq9GA==", + "dependencies": { + "@module-federation/sdk": "0.1.21", + "find-pkg": "2.0.0", + "fs-extra": "9.1.0" + } }, - "node_modules/@mui/private-theming": { - "version": "5.16.8", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.8.tgz", - "integrity": "sha512-3Vl9yFVLU6T3CFtxRMQTcJ60Ijv7wxQi4yjH92+9YXcsqvVspeIYoocqNoIV/1bXGYfyWu5zrCmwQVHaGY7bug==", - "license": "MIT", + "node_modules/@module-federation/manifest": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.1.21.tgz", + "integrity": "sha512-L4iz5Y3qKJzz0bEAG6bDpPy+hHToHyW5xk8XhLJWFGVuXHjEHBl+cvE8tUAZkuWrhHrEmZBGHeHFxlDvdCKMjg==", + "dependencies": { + "@module-federation/dts-plugin": "0.1.21", + "@module-federation/managers": "0.1.21", + "@module-federation/sdk": "0.1.21", + "chalk": "3.0.0", + "find-pkg": "2.0.0" + } + }, + "node_modules/@module-federation/rspack": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.1.21.tgz", + "integrity": "sha512-30bmfb7tCQ8Uu4UKbapoSNNo+52a/rF3Nkje3/lv7xcmW/HGC2+/XJvckITo28jWsl5bzYe+pAu7XSExEAC0Ew==", + "dependencies": { + "@module-federation/dts-plugin": "0.1.21", + "@module-federation/managers": "0.1.21", + "@module-federation/manifest": "0.1.21", + "@module-federation/runtime-tools": "0.1.21", + "@module-federation/sdk": "0.1.21" + } + }, + "node_modules/@module-federation/runtime": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.1.21.tgz", + "integrity": "sha512-/p4BhZ0SnjJuiL0wwu+FebFgIUJ9vM+oCY7CyprUHImyi/Y23ulI61WNWMVrKQGgdMoXQDQCL8RH4EnrVP2ZFw==", + "dependencies": { + "@module-federation/sdk": "0.1.21" + } + }, + "node_modules/@module-federation/runtime-tools": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.1.21.tgz", + "integrity": "sha512-anayDx/wiL80XhD+CPTVvpYvKc4CNbEIy9fGoOCbMQKaKZhgDDE1461fy5abpgJphtj9NoUgCIpEn/O5fHNIfA==", + "dependencies": { + "@module-federation/runtime": "0.1.21", + "@module-federation/webpack-bundler-runtime": "0.1.21" + } + }, + "node_modules/@module-federation/sdk": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.1.21.tgz", + "integrity": "sha512-r7xPiAm+O4e+8Zvw+8b4ToeD0D0VJD004nHmt+Y8r/l98J2eA6di72Vn1FeyjtQbCrFtiMw3ts/dlqtcmIBipw==" + }, + "node_modules/@module-federation/third-party-dts-extractor": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.1.21.tgz", + "integrity": "sha512-gf+8Ah+/3Fs7jgOycVYDBe6o/tDh/+k7WpaeaQGnRV2p7bkrqfZ07g3IoB/kza1VEJFH4SV3u3VmKD7eB5BjQw==", + "dependencies": { + "find-pkg": "2.0.0", + "fs-extra": "9.1.0", + "resolve": "1.22.8" + } + }, + "node_modules/@module-federation/webpack-bundler-runtime": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.1.21.tgz", + "integrity": "sha512-WJg133fvC8PMm3RRCul9+bzaMgyTYD7QpseDBscIWyKdZB80wMBTXuRKbKMwujLXHEPCJX+rkgGwolECnSuCiA==", + "dependencies": { + "@module-federation/runtime": "0.1.21", + "@module-federation/sdk": "0.1.21" + } + }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.68", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.68.tgz", + "integrity": "sha512-F1JMNeLS9Qhjj3wN86JUQYBtJoXyQvknxlzwNl6eS0ZABo1MiohMONj3/WQzYPSXIKC2bS/ZbyBzdHhi2GnEpA==", "peer": true, "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.16.8", + "@babel/runtime": "^7.26.0", + "@floating-ui/react-dom": "^2.1.1", + "@mui/types": "^7.2.20", + "@mui/utils": "^6.3.0", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.1", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", @@ -1280,7 +1891,8 @@ }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1288,54 +1900,64 @@ } } }, - "node_modules/@mui/styled-engine": { - "version": "5.16.8", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.8.tgz", - "integrity": "sha512-OFdgFf8JczSRs0kvWGdSn0ZeXxWrY0LITDPJ/nAtLEvUUTyrlFaO4il3SECX8ruzvf1VnAxHx4M/4mX9oOn9yA==", - "license": "MIT", + "node_modules/@mui/base/node_modules/@mui/utils": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.0.tgz", + "integrity": "sha512-woOTATWNsTNR3YBh2Ixkj3l5RaxSiGoC9G8gOpYoFw1mZM77LWJeuMHFax7iIW4ahK0Cr35TF9DKtrafJmOmNQ==", "peer": true, "dependencies": { - "@babel/runtime": "^7.23.9", - "@emotion/cache": "^11.11.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.21", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { + "@types/react": { "optional": true } } }, - "node_modules/@mui/system": { - "version": "5.16.8", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.8.tgz", - "integrity": "sha512-L32TaFDFpGIi1g6ysRtmhc9zDgrlxDXu3NlrGE8gAsQw/ziHrPdr0PNr20O0POUshA1q14W4dNZ/z0Nx2F9lhA==", - "license": "MIT", + "node_modules/@mui/core-downloads-tracker": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.14.tgz", + "integrity": "sha512-sbjXW+BBSvmzn61XyTMun899E7nGPTXwqD9drm1jBUAvWEhJpPFIRxwQQiATWZnd9rvdxtnhhdsDxEGWI0jxqA==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.14.tgz", + "integrity": "sha512-eSXQVCMKU2xc7EcTxe/X/rC9QsV2jUe8eLM3MUCPYbo6V52eCE436akRIvELq/AqZpxx2bwkq7HC0cRhLB+yaw==", "peer": true, "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.16.8", - "@mui/styled-engine": "^5.16.8", + "@mui/core-downloads-tracker": "^5.16.14", + "@mui/system": "^5.16.14", "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.8", + "@mui/utils": "^5.16.14", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.10", "clsx": "^2.1.0", "csstype": "^3.1.3", - "prop-types": "^15.8.1" + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" }, "engines": { "node": ">=12.0.0" @@ -1348,7 +1970,8 @@ "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -1362,34 +1985,43 @@ } } }, - "node_modules/@mui/types": { - "version": "7.2.19", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz", - "integrity": "sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==", - "license": "MIT", + "node_modules/@mui/material/node_modules/@mui/private-theming": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.14.tgz", + "integrity": "sha512-12t7NKzvYi819IO5IapW2BcR33wP/KAVrU8d7gLhGHoAmhDxyXlRoKiRij3TOD8+uzk0B6R9wHUNKi4baJcRNg==", "peer": true, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.16.14", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { "@types/react": { "optional": true } } }, - "node_modules/@mui/utils": { - "version": "5.16.8", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.8.tgz", - "integrity": "sha512-P/yb7BSWallQUeiNGxb+TM8epHteIUC8gzNTdPV2VfKhVY/EnGliHgt5np0GPkjQ7EzwDi/+gBevrAJtf+K94A==", - "license": "MIT", + "node_modules/@mui/material/node_modules/@mui/styled-engine": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.14.tgz", + "integrity": "sha512-UAiMPZABZ7p8mUW4akDV6O7N3+4DatStpXMZwPlt+H/dA0lt67qawN021MNND+4QTpjaiMYxbhKZeQcyWCbuKw==", "peer": true, "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/types": "^7.2.15", - "@types/prop-types": "^15.7.12", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^18.3.1" + "@emotion/cache": "^11.13.5", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" }, "engines": { "node": ">=12.0.0" @@ -1399,58 +2031,46 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { - "@types/react": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { "optional": true } } }, - "node_modules/@mui/utils/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT", - "peer": true - }, - "node_modules/@mui/x-date-pickers": { - "version": "6.20.2", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.20.2.tgz", - "integrity": "sha512-x1jLg8R+WhvkmUETRfX2wC+xJreMii78EXKLl6r3G+ggcAZlPyt0myID1Amf6hvJb9CtR7CgUo8BwR+1Vx9Ggw==", - "license": "MIT", + "node_modules/@mui/material/node_modules/@mui/system": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.14.tgz", + "integrity": "sha512-KBxMwCb8mSIABnKvoGbvM33XHyT+sN0BzEBG+rsSc0lLQGzs7127KWkCA6/H8h6LZ00XpBEME5MAj8mZLiQ1tw==", "peer": true, "dependencies": { - "@babel/runtime": "^7.23.2", - "@mui/base": "^5.0.0-beta.22", - "@mui/utils": "^5.14.16", - "@types/react-transition-group": "^4.4.8", - "clsx": "^2.0.0", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.16.14", + "@mui/styled-engine": "^5.16.14", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.14", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" }, "engines": { - "node": ">=14.0.0" + "node": ">=12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@emotion/react": "^11.9.0", - "@emotion/styled": "^11.8.1", - "@mui/material": "^5.8.6", - "@mui/system": "^5.8.0", - "date-fns": "^2.25.0 || ^3.2.0", - "date-fns-jalali": "^2.13.0-0", - "dayjs": "^1.10.7", - "luxon": "^3.0.2", - "moment": "^2.29.4", - "moment-hijri": "^2.1.2", - "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -1459,994 +2079,5323 @@ "@emotion/styled": { "optional": true }, - "date-fns": { - "optional": true - }, - "date-fns-jalali": { - "optional": true - }, - "dayjs": { - "optional": true - }, - "luxon": { - "optional": true - }, - "moment": { - "optional": true - }, - "moment-hijri": { - "optional": true - }, - "moment-jalaali": { + "@types/react": { "optional": true } } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", + "node_modules/@mui/private-theming": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.0.tgz", + "integrity": "sha512-rNHci8MP6NOdEWAfZ/RBMO5Rhtp1T6fUDMSmingg9F1T6wiUeodIQ+NuTHh2/pMoUSeP9GdHdgMhMmfsXxOMuw==", + "peer": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@babel/runtime": "^7.26.0", + "@mui/utils": "^6.4.0", + "prop-types": "^15.8.1" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "node": ">=14.0.0" }, - "engines": { - "node": ">= 8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@perses-dev/bar-chart": { - "resolved": "BarChart", - "link": true - }, - "node_modules/@perses-dev/components": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@perses-dev/components/-/components-0.49.0.tgz", - "integrity": "sha512-WZY36f2zgxZs5yH2e2swSBmNf+bXrEUHjSVFod+khFukRsNSXMVAAH54BSd5E4kQyFo5ZPWY0x8Dh85adh5INg==", - "license": "Apache-2.0", + "node_modules/@mui/private-theming/node_modules/@mui/utils": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.0.tgz", + "integrity": "sha512-woOTATWNsTNR3YBh2Ixkj3l5RaxSiGoC9G8gOpYoFw1mZM77LWJeuMHFax7iIW4ahK0Cr35TF9DKtrafJmOmNQ==", "peer": true, "dependencies": { - "@codemirror/lang-json": "^6.0.1", - "@fontsource/lato": "^4.5.10", - "@mui/x-date-pickers": "^6.12.1", - "@perses-dev/core": "0.49.0", - "@tanstack/react-table": "^8.19.2", - "@uiw/react-codemirror": "^4.19.1", - "date-fns": "^2.28.0", - "date-fns-tz": "^1.3.7", - "echarts": "5.5.0", - "lodash": "^4.17.21", - "mathjs": "^10.6.4", - "mdi-material-ui": "^7.4.0", - "notistack": "^2.0.5", - "react-colorful": "^5.6.1", - "react-error-boundary": "^3.1.4", - "react-hook-form": "^7.51.3", - "react-virtuoso": "^4.3.6", - "zod": "^3.21.4" + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.21", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^5.15.20", - "react": "^17.0.2 || ^18.0.0", - "react-dom": "^17.0.2 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@perses-dev/core": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@perses-dev/core/-/core-0.49.0.tgz", - "integrity": "sha512-V2rsZ/BEKdfkmUOP21GrWQmvXXGNBG1Xy4LmZ/T4Ce6/ebhAWGcsuvuhNq3XkLcFVICxe1XfoaEJOCtBq9NCnA==", - "license": "Apache-2.0", + "node_modules/@mui/styled-engine": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.0.tgz", + "integrity": "sha512-ek/ZrDujrger12P6o4luQIfRd2IziH7jQod2WMbLqGE03Iy0zUwYmckRTVhRQTLPNccpD8KXGcALJF+uaUQlbg==", "peer": true, "dependencies": { - "date-fns": "^2.28.0", - "lodash": "^4.17.21", - "mathjs": "^10.6.4", - "numbro": "^2.3.6", - "zod": "^3.21.4" + "@babel/runtime": "^7.26.0", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "react": "^17.0.2 || ^18.0.0", - "react-dom": "^17.0.2 || ^18.0.0" + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } } }, - "node_modules/@perses-dev/gauge-chart": { - "resolved": "GaugeChart", - "link": true - }, - "node_modules/@perses-dev/markdown-chart": { - "resolved": "MarkdownChart", - "link": true - }, - "node_modules/@perses-dev/plugin-system": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@perses-dev/plugin-system/-/plugin-system-0.49.0.tgz", - "integrity": "sha512-JY9AzqNODptuHuK7QnkBA1Z5I52Db7UL/l4Bjq11g7mIJH76NtM9SAjPvIycq7g2hvjryjbNhbgqmJqtqOm23Q==", - "license": "Apache-2.0", + "node_modules/@mui/system": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.0.tgz", + "integrity": "sha512-wTDyfRlaZCo2sW2IuOsrjeE5dl0Usrs6J7DxE3GwNCVFqS5wMplM2YeNiV3DO7s53RfCqbho+gJY6xaB9KThUA==", "peer": true, "dependencies": { - "@perses-dev/components": "0.49.0", - "@perses-dev/core": "0.49.0", - "date-fns": "^2.30.0", - "immer": "^9.0.15", - "react-hook-form": "^7.46.1", - "use-immer": "^0.7.0", - "use-query-params": "^2.1.2", - "zod": "^3.22.2" + "@babel/runtime": "^7.26.0", + "@mui/private-theming": "^6.4.0", + "@mui/styled-engine": "^6.4.0", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.0", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^5.15.20", - "@tanstack/react-query": "^4.7.1", - "react": "^17.0.2 || ^18.0.0", - "react-dom": "^17.0.2 || ^18.0.0" + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } } }, - "node_modules/@perses-dev/prometheus": { - "resolved": "Prometheus", - "link": true + "node_modules/@mui/system/node_modules/@mui/utils": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.0.tgz", + "integrity": "sha512-woOTATWNsTNR3YBh2Ixkj3l5RaxSiGoC9G8gOpYoFw1mZM77LWJeuMHFax7iIW4ahK0Cr35TF9DKtrafJmOmNQ==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.21", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@perses-dev/stat-chart": { - "resolved": "StatChart", + "node_modules/@mui/types": { + "version": "7.2.21", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.21.tgz", + "integrity": "sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.14.tgz", + "integrity": "sha512-wn1QZkRzSmeXD1IguBVvJJHV3s6rxJrfb6YuC9Kk6Noh9f8Fb54nUs5JRkKm+BOerRhj5fLg05Dhx/H3Ofb8Mg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "^7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/x-data-grid": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.23.6.tgz", + "integrity": "sha512-NcCZH99ZBLRlCcfLwwhCkVowNZHgjy0XZ/c6EuTRMSZl1UqF8ouwitP1ZfAa1idDIWCHFhxo446U/93aGMqOyQ==", + "dependencies": { + "@babel/runtime": "^7.25.7", + "@mui/utils": "^5.16.6 || ^6.0.0", + "@mui/x-internals": "7.23.6", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "reselect": "^5.1.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0", + "@mui/system": "^5.15.14 || ^6.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/x-date-pickers": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.23.6.tgz", + "integrity": "sha512-jt6rEAYLju3NZe3y2S+I5KcTiSHV79FW0jeNUEUTceg1qsPzseHbND66k3zVF0hO3N2oZtLtPywof6vN5Doe+Q==", + "dependencies": { + "@babel/runtime": "^7.25.7", + "@mui/utils": "^5.16.6 || ^6.0.0", + "@mui/x-internals": "7.23.6", + "@types/react-transition-group": "^4.4.11", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0", + "@mui/system": "^5.15.14 || ^6.0.0", + "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0", + "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2 || ^3.0.0", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, + "node_modules/@mui/x-internals": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.23.6.tgz", + "integrity": "sha512-hT1Pa4PNCnxwiauPbYMC3p4DiEF1x05Iu4C1MtC/jMJ1LtthymLmTuQ6ZQ53/R9FeqK6sYd6A6noR+vNMjp5DA==", + "dependencies": { + "@babel/runtime": "^7.25.7", + "@mui/utils": "^5.16.6 || ^6.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@perses-dev/bar-chart": { + "resolved": "BarChart", "link": true }, - "node_modules/@perses-dev/static-list-variable": { - "resolved": "StaticListVariable", - "link": true + "node_modules/@perses-dev/components": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@perses-dev/components/-/components-0.49.0.tgz", + "integrity": "sha512-WZY36f2zgxZs5yH2e2swSBmNf+bXrEUHjSVFod+khFukRsNSXMVAAH54BSd5E4kQyFo5ZPWY0x8Dh85adh5INg==", + "peer": true, + "dependencies": { + "@codemirror/lang-json": "^6.0.1", + "@fontsource/lato": "^4.5.10", + "@mui/x-date-pickers": "^6.12.1", + "@perses-dev/core": "0.49.0", + "@tanstack/react-table": "^8.19.2", + "@uiw/react-codemirror": "^4.19.1", + "date-fns": "^2.28.0", + "date-fns-tz": "^1.3.7", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "mathjs": "^10.6.4", + "mdi-material-ui": "^7.4.0", + "notistack": "^2.0.5", + "react-colorful": "^5.6.1", + "react-error-boundary": "^3.1.4", + "react-hook-form": "^7.51.3", + "react-virtuoso": "^4.3.6", + "zod": "^3.21.4" + }, + "peerDependencies": { + "@mui/material": "^5.15.20", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" + } + }, + "node_modules/@perses-dev/components/node_modules/@mui/private-theming": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.14.tgz", + "integrity": "sha512-12t7NKzvYi819IO5IapW2BcR33wP/KAVrU8d7gLhGHoAmhDxyXlRoKiRij3TOD8+uzk0B6R9wHUNKi4baJcRNg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.16.14", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@perses-dev/components/node_modules/@mui/styled-engine": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.14.tgz", + "integrity": "sha512-UAiMPZABZ7p8mUW4akDV6O7N3+4DatStpXMZwPlt+H/dA0lt67qawN021MNND+4QTpjaiMYxbhKZeQcyWCbuKw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.13.5", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@perses-dev/components/node_modules/@mui/system": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.14.tgz", + "integrity": "sha512-KBxMwCb8mSIABnKvoGbvM33XHyT+sN0BzEBG+rsSc0lLQGzs7127KWkCA6/H8h6LZ00XpBEME5MAj8mZLiQ1tw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.16.14", + "@mui/styled-engine": "^5.16.14", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.14", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@perses-dev/components/node_modules/@mui/x-date-pickers": { + "version": "6.20.2", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.20.2.tgz", + "integrity": "sha512-x1jLg8R+WhvkmUETRfX2wC+xJreMii78EXKLl6r3G+ggcAZlPyt0myID1Amf6hvJb9CtR7CgUo8BwR+1Vx9Ggw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.2", + "@mui/base": "^5.0.0-beta.22", + "@mui/utils": "^5.14.16", + "@types/react-transition-group": "^4.4.8", + "clsx": "^2.0.0", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.8.6", + "@mui/system": "^5.8.0", + "date-fns": "^2.25.0 || ^3.2.0", + "date-fns-jalali": "^2.13.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, + "node_modules/@perses-dev/core": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@perses-dev/core/-/core-0.49.0.tgz", + "integrity": "sha512-V2rsZ/BEKdfkmUOP21GrWQmvXXGNBG1Xy4LmZ/T4Ce6/ebhAWGcsuvuhNq3XkLcFVICxe1XfoaEJOCtBq9NCnA==", + "peer": true, + "dependencies": { + "date-fns": "^2.28.0", + "lodash": "^4.17.21", + "mathjs": "^10.6.4", + "numbro": "^2.3.6", + "zod": "^3.21.4" + }, + "peerDependencies": { + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" + } + }, + "node_modules/@perses-dev/gauge-chart": { + "resolved": "GaugeChart", + "link": true + }, + "node_modules/@perses-dev/markdown-chart": { + "resolved": "MarkdownChart", + "link": true + }, + "node_modules/@perses-dev/pie-chart": { + "resolved": "PieChart", + "link": true + }, + "node_modules/@perses-dev/plugin-system": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@perses-dev/plugin-system/-/plugin-system-0.49.0.tgz", + "integrity": "sha512-JY9AzqNODptuHuK7QnkBA1Z5I52Db7UL/l4Bjq11g7mIJH76NtM9SAjPvIycq7g2hvjryjbNhbgqmJqtqOm23Q==", + "peer": true, + "dependencies": { + "@perses-dev/components": "0.49.0", + "@perses-dev/core": "0.49.0", + "date-fns": "^2.30.0", + "immer": "^9.0.15", + "react-hook-form": "^7.46.1", + "use-immer": "^0.7.0", + "use-query-params": "^2.1.2", + "zod": "^3.22.2" + }, + "peerDependencies": { + "@mui/material": "^5.15.20", + "@tanstack/react-query": "^4.7.1", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" + } + }, + "node_modules/@perses-dev/prometheus": { + "resolved": "Prometheus", + "link": true + }, + "node_modules/@perses-dev/scatter-chart": { + "resolved": "ScatterChart", + "link": true + }, + "node_modules/@perses-dev/stat-chart": { + "resolved": "StatChart", + "link": true + }, + "node_modules/@perses-dev/static-list-variable": { + "resolved": "StaticListVariable", + "link": true + }, + "node_modules/@perses-dev/status-history-chart": { + "resolved": "StatusHistoryChart", + "link": true + }, + "node_modules/@perses-dev/table": { + "resolved": "Table", + "link": true + }, + "node_modules/@perses-dev/time-series-table": { + "resolved": "TimeSeriesTable", + "link": true + }, + "node_modules/@perses-dev/timeseries-chart": { + "resolved": "TimeSeriesChart", + "link": true + }, + "node_modules/@perses-dev/trace-table": { + "resolved": "TraceTable", + "link": true + }, + "node_modules/@perses-dev/tracing-gantt-chart": { + "resolved": "TracingGanttChart", + "link": true + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@prometheus-io/codemirror-promql": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@prometheus-io/codemirror-promql/-/codemirror-promql-0.43.1.tgz", + "integrity": "sha512-voSkygjZ7Rz9RkcRwpbP18KSnZGf3lYCfc1j9ungo8+dEAtoTBEIi11GlbYXTDZsDPtbFjYAAApMhFIpK9QvPA==", + "dependencies": { + "@prometheus-io/lezer-promql": "0.43.1", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@codemirror/autocomplete": "^6.4.0", + "@codemirror/language": "^6.3.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.1.1", + "@codemirror/view": "^6.4.0", + "@lezer/common": "^1.0.1" + } + }, + "node_modules/@prometheus-io/lezer-promql": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@prometheus-io/lezer-promql/-/lezer-promql-0.43.1.tgz", + "integrity": "sha512-++pW9w+zASqH7yCSqwb+Q7G7MncG91mWf+ADcqnGMjLRexPOIohxTC1Un1sOTz9oQ6W+9wnEooiNWznNTnTCOQ==", + "peerDependencies": { + "@lezer/highlight": "^1.1.2", + "@lezer/lr": "^1.2.3" + } + }, + "node_modules/@remix-run/router": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", + "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rsbuild/core": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-1.1.13.tgz", + "integrity": "sha512-XBL2hrin8731W6iTGGL+x3cv07n4vm2D7u6XHRwtQkRfySzAqGx7ThlQLdNX/dJwfsoQrYQuWl/qzaljjXtGtg==", + "dev": true, + "dependencies": { + "@rspack/core": "1.1.8", + "@rspack/lite-tapable": "~1.0.1", + "@swc/helpers": "^0.5.15", + "core-js": "~3.39.0" + }, + "bin": { + "rsbuild": "bin/rsbuild.js" + }, + "engines": { + "node": ">=16.7.0" + } + }, + "node_modules/@rsbuild/core/node_modules/@module-federation/runtime": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.5.1.tgz", + "integrity": "sha512-xgiMUWwGLWDrvZc9JibuEbXIbhXg6z2oUkemogSvQ4LKvrl/n0kbqP1Blk669mXzyWbqtSp6PpvNdwaE1aN5xQ==", + "dev": true, + "dependencies": { + "@module-federation/sdk": "0.5.1" + } + }, + "node_modules/@rsbuild/core/node_modules/@module-federation/runtime-tools": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.5.1.tgz", + "integrity": "sha512-nfBedkoZ3/SWyO0hnmaxuz0R0iGPSikHZOAZ0N/dVSQaIzlffUo35B5nlC2wgWIc0JdMZfkwkjZRrnuuDIJbzg==", + "dev": true, + "dependencies": { + "@module-federation/runtime": "0.5.1", + "@module-federation/webpack-bundler-runtime": "0.5.1" + } + }, + "node_modules/@rsbuild/core/node_modules/@module-federation/sdk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.5.1.tgz", + "integrity": "sha512-exvchtjNURJJkpqjQ3/opdbfeT2wPKvrbnGnyRkrwW5o3FH1LaST1tkiNviT6OXTexGaVc2DahbdniQHVtQ7pA==", + "dev": true + }, + "node_modules/@rsbuild/core/node_modules/@module-federation/webpack-bundler-runtime": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.5.1.tgz", + "integrity": "sha512-mMhRFH0k2VjwHt3Jol9JkUsmI/4XlrAoBG3E0o7HoyoPYv1UFOWyqAflfANcUPgbYpvqmyLzDcO+3IT36LXnrA==", + "dev": true, + "dependencies": { + "@module-federation/runtime": "0.5.1", + "@module-federation/sdk": "0.5.1" + } + }, + "node_modules/@rsbuild/core/node_modules/@rspack/binding": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.1.8.tgz", + "integrity": "sha512-+/JzXx1HctfgPj+XtsCTbRkxiaOfAXGZZLEvs7jgp04WgWRSZ5u97WRCePNPvy+sCfOEH/2zw2ZK36Z7oQRGhQ==", + "dev": true, + "optionalDependencies": { + "@rspack/binding-darwin-arm64": "1.1.8", + "@rspack/binding-darwin-x64": "1.1.8", + "@rspack/binding-linux-arm64-gnu": "1.1.8", + "@rspack/binding-linux-arm64-musl": "1.1.8", + "@rspack/binding-linux-x64-gnu": "1.1.8", + "@rspack/binding-linux-x64-musl": "1.1.8", + "@rspack/binding-win32-arm64-msvc": "1.1.8", + "@rspack/binding-win32-ia32-msvc": "1.1.8", + "@rspack/binding-win32-x64-msvc": "1.1.8" + } + }, + "node_modules/@rsbuild/core/node_modules/@rspack/binding-darwin-arm64": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.1.8.tgz", + "integrity": "sha512-I7avr471ghQ3LAqKm2fuXuJPLgQ9gffn5Q4nHi8rsukuZUtiLDPfYzK1QuupEp2JXRWM1gG5lIbSUOht3cD6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rsbuild/core/node_modules/@rspack/binding-darwin-x64": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.1.8.tgz", + "integrity": "sha512-vfqf/c+mcx8rr1M8LnqKmzDdnrgguflZnjGerBLjNerAc+dcUp3lCvNxRIvZ2TkSZZBW8BpCMgjj3n70CZ4VLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rsbuild/core/node_modules/@rspack/binding-linux-arm64-gnu": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.8.tgz", + "integrity": "sha512-lZlO/rAJSeozi+qtVLkGSXfe+riPawCwM4FsrflELfNlvvEXpANwtrdJ+LsaNVXcgvhh50ZX2KicTdmx9G2b6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rsbuild/core/node_modules/@rspack/binding-linux-arm64-musl": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.8.tgz", + "integrity": "sha512-bX7exULSZwy8xtDh6Z65b6sRC4uSxGuyvSLCEKyhmG6AnJkg0gQMxk3hoO0hWnyGEZgdJEn+jEhk0fjl+6ZRAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rsbuild/core/node_modules/@rspack/binding-linux-x64-gnu": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.8.tgz", + "integrity": "sha512-2Prw2USgTJ3aLdLExfik8pAwAHbX4MZrACBGEmR7Vbb56kLjC+++fXkciRc50pUDK4JFr1VQ7eNZrJuDR6GG6Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rsbuild/core/node_modules/@rspack/binding-linux-x64-musl": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.8.tgz", + "integrity": "sha512-bnVGB/mQBKEdzOU/CPmcOE3qEXxGOGGW7/i6iLl2MamVOykJq8fYjL9j86yi6L0r009ja16OgWckykQGc4UqGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rsbuild/core/node_modules/@rspack/binding-win32-arm64-msvc": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.8.tgz", + "integrity": "sha512-u+na3gxhzeksm4xZyAzn1+XWo5a5j7hgWA/KcFPDQ8qQNkRknx4jnQMxVtcZ9pLskAYV4AcOV/AIximx7zvv8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rsbuild/core/node_modules/@rspack/binding-win32-ia32-msvc": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.1.8.tgz", + "integrity": "sha512-FijUxym1INd5fFHwVCLuVP8XEAb4Sk1sMwEEQUlugiDra9ZsLaPw4OgPGxbxkD6SB0DeUz9Zq46Xbcf6d3OgfA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rsbuild/core/node_modules/@rspack/binding-win32-x64-msvc": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.8.tgz", + "integrity": "sha512-SBzIcND4qpDt71jlu1MCDxt335tqInT3YID9V4DoQ4t8wgM/uad7EgKOWKTK6vc2RRaOIShfS2XzqjNUxPXh4w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rsbuild/core/node_modules/@rspack/core": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.1.8.tgz", + "integrity": "sha512-pcZtcj5iXLCuw9oElTYC47bp/RQADm/MMEb3djHdwJuSlFWfWPQi5QFgJ/lJAxIW9UNHnTFrYtytycfjpuoEcA==", + "dev": true, + "dependencies": { + "@module-federation/runtime-tools": "0.5.1", + "@rspack/binding": "1.1.8", + "@rspack/lite-tapable": "1.0.1", + "caniuse-lite": "^1.0.30001616" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.1" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@rsbuild/plugin-react": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rsbuild/plugin-react/-/plugin-react-1.1.0.tgz", + "integrity": "sha512-uqdRoV2V91G1XIA14dAmxqYTlTDVf0ktpE7TgwG29oQ2j+DerF1kh29WPHK9HvGE34JTfaBrsme2Zmb6bGD0cw==", + "dev": true, + "dependencies": { + "@rspack/plugin-react-refresh": "~1.0.0", + "react-refresh": "^0.16.0" + }, + "peerDependencies": { + "@rsbuild/core": "1.x" + } + }, + "node_modules/@rspack/binding": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-0.6.5.tgz", + "integrity": "sha512-uHg6BYS9Uvs5Nxm0StpRX1eqx3I1SEPFhkCfh+HSbFS8ty11mKHjUZn1lYFxLBFypJ3DHtlTM3RZ4g7tmwohAQ==", + "dev": true, + "optionalDependencies": { + "@rspack/binding-darwin-arm64": "0.6.5", + "@rspack/binding-darwin-x64": "0.6.5", + "@rspack/binding-linux-arm64-gnu": "0.6.5", + "@rspack/binding-linux-arm64-musl": "0.6.5", + "@rspack/binding-linux-x64-gnu": "0.6.5", + "@rspack/binding-linux-x64-musl": "0.6.5", + "@rspack/binding-win32-arm64-msvc": "0.6.5", + "@rspack/binding-win32-ia32-msvc": "0.6.5", + "@rspack/binding-win32-x64-msvc": "0.6.5" + } + }, + "node_modules/@rspack/binding-darwin-arm64": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-0.6.5.tgz", + "integrity": "sha512-5Zbs3buzF80MZoWnnpm/ZqQ2ZLKWjmmy94gDMeJhG39lKcpK2J2NyDXVis2ZSg7uUvKyJ662BEgIE1AnTWjnYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rspack/binding-darwin-x64": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-0.6.5.tgz", + "integrity": "sha512-oA1R0OF8r7y8+oLynnZC9EgysLoOBuu1yYG90gHmrkdzRjjmYe4auNhuSLLqF+WOqXw/zGSujiUbnVMjLEWIBg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rspack/binding-linux-arm64-gnu": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.6.5.tgz", + "integrity": "sha512-xK2Ji9yCJSZE5HSRBS7R67HPahYd0WR16NefycrkmIEDR28B2T5CnvbqyNivnu7Coy1haHWisgfTV/NbjLd5fA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-linux-arm64-musl": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.6.5.tgz", + "integrity": "sha512-nPDUf6TkzJWxqi6gQQz+Ypd2BPDiufh0gd0yFExIZyguE93amVbzJEfKeCQdvHZL5W/9XaYJoDKSOuCwMdLhiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-linux-x64-gnu": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.6.5.tgz", + "integrity": "sha512-KT4GBPra7ge5oHSblfM74oRgW10MKdKhyJGEKFWqRezzul8i9SHElFzcE/w6qoOOLMgYPoVc/nybRqsJp9koZg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-linux-x64-musl": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-0.6.5.tgz", + "integrity": "sha512-VnIzpFjzT4vkfUKPqyH4BiHJ6AMqtoeu7tychga2HpSudqCG8no4eIH2qRs9anGeuRkwb9x3uBC/1AIIiWSMsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-win32-arm64-msvc": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.6.5.tgz", + "integrity": "sha512-V44hlcK7htG1pA/fHCc1XDGmItu7v8qQObssl/yGAn4+ZlvP6/pxPy8y5ZVwnR3NXTRzPezMvbnKGb4GxBphlw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rspack/binding-win32-ia32-msvc": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.6.5.tgz", + "integrity": "sha512-M4xrJDx5EcAtZ02R9Y4yJB5KVCUdQIbAF/1gDGrXZ5PQUujaNzsIdISUvNfxpfkqe0Shj6SKOTqWm8yte3ecrQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rspack/binding-win32-x64-msvc": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.6.5.tgz", + "integrity": "sha512-aFcBygJsClx0FozVo7zMp9OUte7MlgyBpQGnS2MZgd0kSnuZTyaUcdRiWKehP5lrPPij/ZWNJbiz5O6VNzpg3w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rspack/core": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@rspack/core/-/core-0.6.5.tgz", + "integrity": "sha512-jm0YKUZQCetccdufBfpkfSHE7BOlirrn0UmXv9C+69g8ikl9Jf4Jfr31meDWX5Z3vwZlpdryA7fUH2cblUXoBw==", + "dev": true, + "dependencies": { + "@module-federation/runtime-tools": "0.1.6", + "@rspack/binding": "0.6.5", + "caniuse-lite": "^1.0.30001616", + "enhanced-resolve": "5.12.0", + "tapable": "2.2.1", + "webpack-sources": "3.2.3" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.1" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@rspack/core/node_modules/@module-federation/runtime": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.1.6.tgz", + "integrity": "sha512-nj6a+yJ+QxmcE89qmrTl4lphBIoAds0PFPVGnqLRWflwAP88jrCcrrTqRhARegkFDL+wE9AE04+h6jzlbIfMKg==", + "dev": true, + "dependencies": { + "@module-federation/sdk": "0.1.6" + } + }, + "node_modules/@rspack/core/node_modules/@module-federation/runtime-tools": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.1.6.tgz", + "integrity": "sha512-7ILVnzMIa0Dlc0Blck5tVZG1tnk1MmLnuZpLOMpbdW+zl+N6wdMjjHMjEZFCUAJh2E5XJ3BREwfX8Ets0nIkLg==", + "dev": true, + "dependencies": { + "@module-federation/runtime": "0.1.6", + "@module-federation/webpack-bundler-runtime": "0.1.6" + } + }, + "node_modules/@rspack/core/node_modules/@module-federation/sdk": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.1.6.tgz", + "integrity": "sha512-qifXpyYLM7abUeEOIfv0oTkguZgRZuwh89YOAYIZJlkP6QbRG7DJMQvtM8X2yHXm9PTk0IYNnOJH0vNQCo6auQ==", + "dev": true + }, + "node_modules/@rspack/core/node_modules/@module-federation/webpack-bundler-runtime": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.1.6.tgz", + "integrity": "sha512-K5WhKZ4RVNaMEtfHsd/9CNCgGKB0ipbm/tgweNNeC11mEuBTNxJ09Y630vg3WPkKv9vfMCuXg2p2Dk+Q/KWTSA==", + "dev": true, + "dependencies": { + "@module-federation/runtime": "0.1.6", + "@module-federation/sdk": "0.1.6" + } + }, + "node_modules/@rspack/lite-tapable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.0.1.tgz", + "integrity": "sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==", + "dev": true, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@rspack/plugin-react-refresh": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rspack/plugin-react-refresh/-/plugin-react-refresh-1.0.1.tgz", + "integrity": "sha512-KSBc3bsr3mrAPViv7w9MpE9KEWm6q87EyRXyHlRfJ9PpQ56NbX9KZ7AXo7jPeECb0q5sfpM2PSEf+syBiMgLSw==", + "dev": true, + "dependencies": { + "error-stack-parser": "^2.1.4", + "html-entities": "^2.5.2" + }, + "peerDependencies": { + "react-refresh": ">=0.10.0 <1.0.0" + }, + "peerDependenciesMeta": { + "react-refresh": { + "optional": true + } + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@swc/core": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.10.tgz", + "integrity": "sha512-l0xrFwBQ9atizhmV94yC2nwcecTk/oftofwMNPiFMGe56dqdmi2ArHaTV3PCtMlgaUH6rGCehoRMt5OrCI1ktg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.12" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.7.10", + "@swc/core-darwin-x64": "1.7.10", + "@swc/core-linux-arm-gnueabihf": "1.7.10", + "@swc/core-linux-arm64-gnu": "1.7.10", + "@swc/core-linux-arm64-musl": "1.7.10", + "@swc/core-linux-x64-gnu": "1.7.10", + "@swc/core-linux-x64-musl": "1.7.10", + "@swc/core-win32-arm64-msvc": "1.7.10", + "@swc/core-win32-ia32-msvc": "1.7.10", + "@swc/core-win32-x64-msvc": "1.7.10" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.10.tgz", + "integrity": "sha512-TYp4x/9w/C/yMU1olK5hTKq/Hi7BjG71UJ4V1U1WxI1JA3uokjQ/GoktDfmH5V5pX4dgGSOJwUe2RjoN8Z/XnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.10.tgz", + "integrity": "sha512-P3LJjAWh5yLc6p5IUwV5LgRfA3R1oDCZDMabYyb2BVQuJTD4MfegW9DhBcUUF5dhBLwq3191KpLVzE+dLTbiXw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.10.tgz", + "integrity": "sha512-yGOFjE7w/akRTmqGY3FvWYrqbxO7OB2N2FHj2LO5HtzXflfoABb5RyRvdEquX+17J6mEpu4EwjYNraTD/WHIEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.10.tgz", + "integrity": "sha512-SPWsgWHfdWKKjLrYlvhxcdBJ7Ruy6crJbPoE9NfD95eJEjMnS2yZTqj2ChFsY737WeyhWYlHzgYhYOVCp83YwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.10.tgz", + "integrity": "sha512-PUi50bkNqnBL3Z/Zq6jSfwgN9A/taA6u2Zou0tjDJi7oVdpjdr7SxNgCGzMJ/nNg5D/IQn1opM1jktMvpsPAuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.10.tgz", + "integrity": "sha512-Sc+pY55gknCAmBQBR6DhlA7jZSxHaLSDb5Sevzi6DOFMXR79NpA6zWTNKwp1GK2AnRIkbAfvYLgOxS5uWTFVpg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.10.tgz", + "integrity": "sha512-g5NKx2LXaGd0K26hmEts1Cvb7ptIvq3MHSgr6/D1tRPcDZw1Sp0dYsmyOv0ho4F5GOJyiCooG3oE9FXdb7jIpQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.10.tgz", + "integrity": "sha512-plRIsOcfy9t9Q/ivm5DA7I0HaIvfAWPbI+bvVRrr3C/1K2CSqnqZJjEWOAmx2LiyipijNnEaFYuLBp0IkGuJpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.10.tgz", + "integrity": "sha512-GntrVNT23viHtbfzmlK8lfBiKeajH24GzbDT7qXhnoO20suUPcyYZxyvCb4gWM2zu8ZBTPHNlqfrNsriQCZ+lQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.10.tgz", + "integrity": "sha512-uXIF8GuSappe1imm6Lf7pHGepfCBjDQlS+qTqvEGE0wZAsL1IVATK9P/cH/OCLfJXeQDTLeSYmrpwjtXNt46tQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "dev": true, + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@swc/jest": { + "version": "0.2.37", + "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.37.tgz", + "integrity": "sha512-CR2BHhmXKGxTiFr21DYPRHQunLkX3mNIFGFkxBGji6r9uyIR5zftTOVYj1e0sFNMV2H7mf/+vpaglqaryBtqfQ==", + "dev": true, + "dependencies": { + "@jest/create-cache-key-function": "^29.7.0", + "@swc/counter": "^0.1.3", + "jsonc-parser": "^3.2.0" + }, + "engines": { + "npm": ">= 7.0.0" + }, + "peerDependencies": { + "@swc/core": "*" + } + }, + "node_modules/@swc/types": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz", + "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@tanstack/query-core": { + "version": "4.36.1", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz", + "integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==", + "peer": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "4.36.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.36.1.tgz", + "integrity": "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==", + "peer": true, + "dependencies": { + "@tanstack/query-core": "4.36.1", + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@tanstack/react-table": { + "version": "8.20.6", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.6.tgz", + "integrity": "sha512-w0jluT718MrOKthRcr2xsjqzx+oEM7B7s/XXyfs19ll++hlId3fjTm+B2zrR3ijpANpkzBAr15j1XGVOMxpggQ==", + "dependencies": { + "@tanstack/table-core": "8.20.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.20.5.tgz", + "integrity": "sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", + "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + }, + "node_modules/@testing-library/react": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.1.0.tgz", + "integrity": "sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/color-hash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/color-hash/-/color-hash-2.0.0.tgz", + "integrity": "sha512-wVZU2AthjkuxcK8IQl2lpVzWZxu/nuOoQfEBv0cxsbV8mVlCiPExNaw9/Z1PBC0ostupYr8KbTmT2J4+qrOW7w==", + "dev": true + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/lodash": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.6.tgz", + "integrity": "sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "peer": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" + }, + "node_modules/@types/react": { + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "dev": true, + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@uiw/codemirror-extensions-basic-setup": { + "version": "4.23.7", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.23.7.tgz", + "integrity": "sha512-9/2EUa1Lck4kFKkR2BkxlZPpgD/EWuKHnOlysf1yHKZGraaZmZEaUw+utDK4QcuJc8Iz097vsLz4f4th5EU27g==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@codemirror/autocomplete": ">=6.0.0", + "@codemirror/commands": ">=6.0.0", + "@codemirror/language": ">=6.0.0", + "@codemirror/lint": ">=6.0.0", + "@codemirror/search": ">=6.0.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/view": ">=6.0.0" + } + }, + "node_modules/@uiw/react-codemirror": { + "version": "4.23.7", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.23.7.tgz", + "integrity": "sha512-Nh/0P6W+kWta+ARp9YpnKPD9ick5teEnwmtNoPQnyd6NPv0EQP3Ui4YmRVNj1nkUEo+QjrAUaEfcejJ2up/HZA==", + "dependencies": { + "@babel/runtime": "^7.18.6", + "@codemirror/commands": "^6.1.0", + "@codemirror/state": "^6.1.1", + "@codemirror/theme-one-dark": "^6.0.0", + "@uiw/codemirror-extensions-basic-setup": "4.23.7", + "codemirror": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.11.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/theme-one-dark": ">=6.0.0", + "@codemirror/view": ">=6.0.0", + "codemirror": ">=6.0.0", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, + "node_modules/bind-event-listener": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bind-event-listener/-/bind-event-listener-3.0.0.tgz", + "integrity": "sha512-PJvH288AWQhKs2v9zyfYdPzlPqf5bXbGMmhmUIY9x4dAUGIWgomO771oBQNwJnMQSnUIXhKu6sgzpBRXTlvb8Q==" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/cache-content-type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", + "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", + "dependencies": { + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001692", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", + "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-hash": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-hash/-/color-hash-2.0.2.tgz", + "integrity": "sha512-6exeENAqBTuIR1wIo36mR8xVVBv6l1hSLd7Qmvf6158Ld1L15/dbahR9VUOiX7GmGJBCnQyS0EY+I8x+wa7egg==" + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/complex.js": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.2.tgz", + "integrity": "sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "peer": true + }, + "node_modules/cookies": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", + "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cookies/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/core-js": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "peer": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/date-fns-tz": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.8.tgz", + "integrity": "sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==", + "peerDependencies": { + "date-fns": ">=2.0.0" + } + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dompurify": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.3.tgz", + "integrity": "sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/echarts": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz", + "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.5.0" + } + }, + "node_modules/echarts/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.82", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.82.tgz", + "integrity": "sha512-Zq16uk1hfQhyGx5GpwPAYDwddJuSGhtRhgOA2mCxANYaDT79nAeGnaXogMGng4KqLaJUVnOnuL0+TDop9nLOiA==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-inject": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/error-inject/-/error-inject-1.0.0.tgz", + "integrity": "sha512-JM8N6PytDbmIYm1IhPWlo8vr3NtfjhDY/1MhD/a5b/aad/USE8a0+NsqE9d5n+GVGmuNkPQWm4bFQWv18d8tMg==" + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.2.tgz", + "integrity": "sha512-1yI3/hf35wmlq66C8yOyrujQnel+v5l1Vop5Cl2I6ylyNTT1JbuUUnV3/41PzwTzcyDp/oF0jWE3HXvcH5AQOQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", + "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-file-up": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-2.0.1.tgz", + "integrity": "sha512-qVdaUhYO39zmh28/JLQM5CoYN9byEOKEH4qfa8K1eNV17W0UUMJ9WgbR/hHFH+t5rcl+6RTb5UC7ck/I+uRkpQ==", + "dependencies": { + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-2.0.0.tgz", + "integrity": "sha512-WgZ+nKbELDa6N3i/9nrHeNznm+lY3z4YfhDDWgW+5P0pdmMj26bxaxU11ookgY3NyP9GC7HvZ9etp0jRFqGEeQ==", + "dependencies": { + "find-file-up": "^2.0.1" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/@perses-dev/timeseries-chart": { - "resolved": "TimeSeriesChart", - "link": true + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "peer": true }, - "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "license": "MIT", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@prometheus-io/codemirror-promql": { - "version": "0.43.1", - "resolved": "https://registry.npmjs.org/@prometheus-io/codemirror-promql/-/codemirror-promql-0.43.1.tgz", - "integrity": "sha512-voSkygjZ7Rz9RkcRwpbP18KSnZGf3lYCfc1j9ungo8+dEAtoTBEIi11GlbYXTDZsDPtbFjYAAApMhFIpK9QvPA==", - "license": "Apache-2.0", + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, "dependencies": { - "@prometheus-io/lezer-promql": "0.43.1", - "lru-cache": "^6.0.0" + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@codemirror/autocomplete": "^6.4.0", - "@codemirror/language": "^6.3.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/state": "^6.1.1", - "@codemirror/view": "^6.4.0", - "@lezer/common": "^1.0.1" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/@prometheus-io/lezer-promql": { - "version": "0.43.1", - "resolved": "https://registry.npmjs.org/@prometheus-io/lezer-promql/-/lezer-promql-0.43.1.tgz", - "integrity": "sha512-++pW9w+zASqH7yCSqwb+Q7G7MncG91mWf+ADcqnGMjLRexPOIohxTC1Un1sOTz9oQ6W+9wnEooiNWznNTnTCOQ==", - "license": "Apache-2.0", - "peerDependencies": { - "@lezer/highlight": "^1.1.2", - "@lezer/lr": "^1.2.3" + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/@rsbuild/core": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-1.1.10.tgz", - "integrity": "sha512-G0aVnoMSIZ4PNcW07tKtsOSoID9M03EAnCThRmUWMj1RXDqhbGje6AFBwGun9uz63bdxYEbEp9C8wH7dGi8aYQ==", + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, - "license": "MIT", "dependencies": { - "@rspack/core": "~1.1.6", - "@rspack/lite-tapable": "~1.0.1", - "@swc/helpers": "^0.5.15", - "core-js": "~3.39.0" - }, - "bin": { - "rsbuild": "bin/rsbuild.js" - }, - "engines": { - "node": ">=16.7.0" + "is-callable": "^1.1.3" } }, - "node_modules/@rsbuild/plugin-react": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rsbuild/plugin-react/-/plugin-react-1.1.0.tgz", - "integrity": "sha512-uqdRoV2V91G1XIA14dAmxqYTlTDVf0ktpE7TgwG29oQ2j+DerF1kh29WPHK9HvGE34JTfaBrsme2Zmb6bGD0cw==", - "dev": true, - "license": "MIT", + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dependencies": { - "@rspack/plugin-react-refresh": "~1.0.0", - "react-refresh": "^0.16.0" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, - "peerDependencies": { - "@rsbuild/core": "1.x" + "engines": { + "node": ">= 6" } }, - "node_modules/@rspack/binding": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.1.6.tgz", - "integrity": "sha512-vfeBEgGOYVwqj5cQjGyvdfrr/BEihAHlyIsobL98FZjTF0uig+bj2yJUH5Ib5F0BpIUKVG3Pw0IjlUBqcVpZsQ==", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "@rspack/binding-darwin-arm64": "1.1.6", - "@rspack/binding-darwin-x64": "1.1.6", - "@rspack/binding-linux-arm64-gnu": "1.1.6", - "@rspack/binding-linux-arm64-musl": "1.1.6", - "@rspack/binding-linux-x64-gnu": "1.1.6", - "@rspack/binding-linux-x64-musl": "1.1.6", - "@rspack/binding-win32-arm64-msvc": "1.1.6", - "@rspack/binding-win32-ia32-msvc": "1.1.6", - "@rspack/binding-win32-x64-msvc": "1.1.6" + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" } }, - "node_modules/@rspack/binding-darwin-arm64": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.1.6.tgz", - "integrity": "sha512-x9dxm2yyiMuL1FBwvWNNMs2/mEUJmRoSRgYb8pblR7HDaTRORrjBFCqhaYlGyAqtQaeUy7o2VAQlE0BavIiFYA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/@rspack/binding-darwin-x64": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.1.6.tgz", - "integrity": "sha512-o0seilveftGiDjy3VPxug20HmAgYyQbNEuagR3i93/t/PT/eWXHnik+C1jjwqcivZL1Zllqvy4tbZw393aROEQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/@rspack/binding-linux-arm64-gnu": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.6.tgz", - "integrity": "sha512-4atnoknJx/c3KaQElsMIxHMpPf2jcRRdWsH/SdqJIRSrkWWakMK9Yv4TFwH680I4HDTMf1XLboMVScHzW8e+Mg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, - "node_modules/@rspack/binding-linux-arm64-musl": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.6.tgz", - "integrity": "sha512-7QMtwUtgFpt3/Y3/X18fSyN+kk4H8ZnZ8tDzQskVWc/j2AQYShZq56XQYqrhClzwujcCVAHauIQ2eiuJ2ASGag==", - "cpu": [ - "arm64" - ], + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "license": "MIT", + "hasInstallScript": true, "optional": true, "os": [ - "linux" - ] - }, - "node_modules/@rspack/binding-linux-x64-gnu": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.6.tgz", - "integrity": "sha512-MTjDEfPn4TwHoqs5d5Fck06kmXiTHZctGIcRVfrpg0RK0r1NLEHN+oosavRZ9c9H70f34+NmcHk+/qvV4c8lWg==", - "cpu": [ - "x64" + "darwin" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "node_modules/@rspack/binding-linux-x64-musl": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.6.tgz", - "integrity": "sha512-LqDw7PTVr/4ZuGA0izgDQfamfr72USFHltR1Qhy2YVC3JmDmhG/pQi13LHcOLVaGH1xoeyCmEPNJpVizzDxSjg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rspack/binding-win32-arm64-msvc": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.6.tgz", - "integrity": "sha512-RHApLM93YN0WdHpS35u2cm7VCqZ8Yg3CrNRL16VJtyT9e6MBqeScoe4XIgIWKPm7edFyedYAjLX0wQOApwfjkg==", - "cpu": [ - "arm64" - ], + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rspack/binding-win32-ia32-msvc": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.1.6.tgz", - "integrity": "sha512-Y6lx4q0eJawRfMPBo/AclTJAPTZ325DSPFBQJB3TnWh9Z2X7P7pQcYc8PHDmfDuYRIdg5WRsQRvVxihSvF7v8w==", - "cpu": [ - "ia32" - ], + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@rspack/binding-win32-x64-msvc": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.6.tgz", - "integrity": "sha512-UuCsfhC/yNuU7xLASOxNXcmsXi2ZvBX14GkxvcdChw6q7IIGNYUKXo1zgR8C1PE/6qDSxmLxbRMS+71d0H3HQg==", - "cpu": [ - "x64" - ], + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@rspack/core": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.1.6.tgz", - "integrity": "sha512-q0VLphOF5VW2FEG7Vbdq3Ke4I74FbELE/8xmKghSalFtULLZ44SoSz8lyotfMim9GXIRFhDokAaH8WICmPxG+g==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "license": "MIT", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dependencies": { - "@module-federation/runtime-tools": "0.5.1", - "@rspack/binding": "1.1.6", - "@rspack/lite-tapable": "1.0.1", - "caniuse-lite": "^1.0.30001616" + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@swc/helpers": ">=0.5.1" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@rspack/lite-tapable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.0.1.tgz", - "integrity": "sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, - "license": "MIT", "engines": { - "node": ">=16.0.0" + "node": ">=8.0.0" } }, - "node_modules/@rspack/plugin-react-refresh": { + "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rspack/plugin-react-refresh/-/plugin-react-refresh-1.0.1.tgz", - "integrity": "sha512-KSBc3bsr3mrAPViv7w9MpE9KEWm6q87EyRXyHlRfJ9PpQ56NbX9KZ7AXo7jPeECb0q5sfpM2PSEf+syBiMgLSw==", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dependencies": { - "error-stack-parser": "^2.1.4", - "html-entities": "^2.5.2" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, - "peerDependencies": { - "react-refresh": ">=0.10.0 <1.0.0" + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" }, - "peerDependenciesMeta": { - "react-refresh": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@rtsao/scc": { + "node_modules/get-symbol-description": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, - "license": "MIT" + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "Apache-2.0", "dependencies": { - "tslib": "^2.8.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@tanstack/query-core": { - "version": "4.36.1", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz", - "integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==", - "license": "MIT", - "peer": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/@tanstack/react-query": { - "version": "4.36.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.36.1.tgz", - "integrity": "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==", - "license": "MIT", - "peer": true, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { - "@tanstack/query-core": "4.36.1", - "use-sync-external-store": "^1.2.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" + "engines": { + "node": "*" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-native": "*" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } + "engines": { + "node": ">=0.10.0" } }, - "node_modules/@tanstack/react-table": { - "version": "8.20.6", - "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.6.tgz", - "integrity": "sha512-w0jluT718MrOKthRcr2xsjqzx+oEM7B7s/XXyfs19ll++hlId3fjTm+B2zrR3ijpANpkzBAr15j1XGVOMxpggQ==", - "license": "MIT", - "peer": true, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dependencies": { - "@tanstack/table-core": "8.20.5" + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@tanstack/table-core": { - "version": "8.20.5", - "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.20.5.tgz", - "integrity": "sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==", - "license": "MIT", - "peer": true, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/color-hash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/color-hash/-/color-hash-2.0.0.tgz", - "integrity": "sha512-wVZU2AthjkuxcK8IQl2lpVzWZxu/nuOoQfEBv0cxsbV8mVlCiPExNaw9/Z1PBC0ostupYr8KbTmT2J4+qrOW7w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/lodash": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", - "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "license": "MIT", - "peer": true + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "node_modules/@types/react": { - "version": "18.3.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.17.tgz", - "integrity": "sha512-opAQ5no6LqJNo9TqnxBKsgnkIYHozW9KSTlFVoSUJYh1Fl/sswkEoqIugRSm7tbh6pABtYjGAjW+GOS23j8qbw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true }, - "node_modules/@types/react-dom": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", - "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/react-transition-group": { - "version": "4.4.12", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", - "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", - "license": "MIT", - "peer": true, - "peerDependencies": { - "@types/react": "*" + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", - "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, - "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" + "es-define-property": "^1.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, - "license": "BSD-2-Clause", - "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4" + "dunder-proto": "^1.0.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" - }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", - "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", - "dev": true, - "license": "MIT", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "has-symbols": "^1.0.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || >=20.0.0" + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "engines": { + "node": ">= 0.4" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dependencies": { + "parse-passwd": "^1.0.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, - "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" + "whatwg-encoding": "^2.0.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" + "node": ">=12" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", + "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" + "deep-equal": "~1.0.1", + "http-errors": "~1.8.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">= 0.8" } }, - "node_modules/@uiw/codemirror-extensions-basic-setup": { - "version": "4.23.7", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.23.7.tgz", - "integrity": "sha512-9/2EUa1Lck4kFKkR2BkxlZPpgD/EWuKHnOlysf1yHKZGraaZmZEaUw+utDK4QcuJc8Iz097vsLz4f4th5EU27g==", - "license": "MIT", - "peer": true, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" }, - "peerDependencies": { - "@codemirror/autocomplete": ">=6.0.0", - "@codemirror/commands": ">=6.0.0", - "@codemirror/language": ">=6.0.0", - "@codemirror/lint": ">=6.0.0", - "@codemirror/search": ">=6.0.0", - "@codemirror/state": ">=6.0.0", - "@codemirror/view": ">=6.0.0" + "engines": { + "node": ">= 0.6" } }, - "node_modules/@uiw/react-codemirror": { - "version": "4.23.7", - "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.23.7.tgz", - "integrity": "sha512-Nh/0P6W+kWta+ARp9YpnKPD9ick5teEnwmtNoPQnyd6NPv0EQP3Ui4YmRVNj1nkUEo+QjrAUaEfcejJ2up/HZA==", - "license": "MIT", - "peer": true, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.18.6", - "@codemirror/commands": "^6.1.0", - "@codemirror/state": "^6.1.1", - "@codemirror/theme-one-dark": "^6.0.0", - "@uiw/codemirror-extensions-basic-setup": "4.23.7", - "codemirror": "^6.0.0" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" - }, - "peerDependencies": { - "@babel/runtime": ">=7.11.0", - "@codemirror/state": ">=6.0.0", - "@codemirror/theme-one-dark": ">=6.0.0", - "@codemirror/view": ">=6.0.0", - "codemirror": ">=6.0.0", - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "engines": { + "node": ">= 6" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", - "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, - "license": "ISC" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">= 0.6" + "node": ">= 6" } }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=0.10.0" } }, - "node_modules/acorn-jsx": { + "node_modules/ignore": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "engines": { + "node": ">= 4" } }, - "node_modules/adm-zip": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", - "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", - "license": "MIT", + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, "engines": { - "node": ">=12.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, - "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "license": "MIT", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=0.8.19" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, - "license": "Apache-2.0", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, "engines": { "node": ">= 0.4" } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -2455,19 +7404,21 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-async-function": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", + "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2476,29 +7427,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "has-bigints": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2507,19 +7442,14 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "node_modules/is-boolean-object": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2528,18 +7458,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, "engines": { "node": ">= 0.4" }, @@ -2547,17 +7470,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "dev": true, - "license": "MIT", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -2566,37 +7484,31 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, - "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2605,36 +7517,22 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "license": "ISC", "engines": { - "node": ">= 4.0.0" + "node": ">=0.10.0" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, - "license": "MIT", "dependencies": { - "possible-typed-array-names": "^1.0.0" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -2643,117 +7541,114 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axe-core": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", - "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "license": "MPL-2.0", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" } }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "dev": true, - "license": "Apache-2.0", + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "license": "MIT", - "peer": true, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=10", - "npm": ">=6" + "node": ">=0.10.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, - "license": "MIT" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/bignumber.js": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", - "license": "MIT", - "peer": true, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { - "node": "*" + "node": ">=0.12.0" } }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/braces": { + "node_modules/is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, "engines": { "node": ">=8" } }, - "node_modules/cache-content-type": { + "node_modules/is-potential-custom-element-name": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", - "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", - "license": "MIT", - "dependencies": { - "mime-types": "^2.1.18", - "ylru": "^1.2.0" - }, - "engines": { - "node": ">= 6.0.0" - } + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "license": "MIT", + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -2762,29 +7657,25 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -2793,1456 +7684,1710 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "license": "MIT", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001689", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001689.tgz", - "integrity": "sha512-CmeR2VBycfa+5/jOfnp/NpWPGd06nf1XYiefUvhXFfZE4GkRc9jv+eGPS4nT558WS/8lYCzV8SlANCIPvbWP1g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/codemirror": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", - "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", - "license": "MIT", - "peer": true, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "which-typed-array": "^1.1.16" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/color-hash": { + "node_modules/is-weakmap": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/color-hash/-/color-hash-2.0.2.tgz", - "integrity": "sha512-6exeENAqBTuIR1wIo36mR8xVVBv6l1hSLd7Qmvf6158Ld1L15/dbahR9VUOiX7GmGJBCnQyS0EY+I8x+wa7egg==", - "license": "MIT" - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/complex.js": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.2.tgz", - "integrity": "sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==", - "license": "MIT", - "peer": true, + "node_modules/is-weakref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2" + }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", "dependencies": { - "safe-buffer": "5.2.1" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "license": "MIT", - "peer": true + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true }, - "node_modules/cookies": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", - "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "keygrip": "~1.1.0" - }, - "engines": { - "node": ">= 0.8" + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" } }, - "node_modules/cookies/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/core-js": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", - "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "license": "MIT", - "peer": true, "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" }, "engines": { "node": ">=10" } }, - "node_modules/crelt": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", - "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", - "license": "MIT", - "peer": true - }, - "node_modules/cron-parser": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", - "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", - "license": "MIT", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, "dependencies": { - "luxon": "^3.2.1" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=10" } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, - "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" }, "engines": { - "node": ">= 8" + "node": ">=10" } }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT", - "peer": true + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, - "license": "BSD-2-Clause" + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "license": "MIT", - "peer": true, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.21.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=0.11" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/date-fns-tz": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.8.tgz", - "integrity": "sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==", - "license": "MIT", - "peer": true, - "peerDependencies": { - "date-fns": ">=2.0.0" + "node_modules/jest-circus/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/date-format": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", - "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", - "license": "MIT", + "node_modules/jest-circus/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { - "node": ">=4.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", + "node_modules/jest-circus/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, "dependencies": { - "ms": "^2.1.3" + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">=6.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { - "supports-color": { + "node-notifier": { "optional": true } } }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "license": "MIT", - "peer": true - }, - "node_modules/deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", - "license": "MIT" - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT" + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, - "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", + "node_modules/jest-config/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, "engines": { - "node": ">=0.4.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "license": "MIT" - }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "license": "MIT", + "node_modules/jest-config/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", + "node_modules/jest-config/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "path-type": "^4.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "esutils": "^2.0.2" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=6.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" + "node_modules/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/dompurify": { - "version": "2.5.8", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.8.tgz", - "integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==", - "license": "(MPL-2.0 OR Apache-2.0)" + "node_modules/jest-diff/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "detect-newline": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/echarts": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz", - "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", - "license": "Apache-2.0", - "peer": true, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, "dependencies": { - "tslib": "2.3.0", - "zrender": "5.5.0" + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/echarts/node_modules/tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", - "license": "0BSD", - "peer": true - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "license": "MIT", - "peer": true, + "node_modules/jest-each/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, "dependencies": { - "is-arrayish": "^0.2.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/error-inject": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/error-inject/-/error-inject-1.0.0.tgz", - "integrity": "sha512-JM8N6PytDbmIYm1IhPWlo8vr3NtfjhDY/1MhD/a5b/aad/USE8a0+NsqE9d5n+GVGmuNkPQWm4bFQWv18d8tMg==", - "license": "MIT" - }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "node_modules/jest-each/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "license": "MIT", - "dependencies": { - "stackframe": "^1.3.4" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/es-abstract": { - "version": "1.23.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.6.tgz", - "integrity": "sha512-Ifco6n3yj2tMZDWNLyloZrytt9lqqlwvS83P3HtaETR0NUOYnIULGGHpktqYGObGy+8wc1okO25p8TjemhImvA==", + "node_modules/jest-each/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", "dev": true, - "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.7", - "get-intrinsic": "^1.2.6", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.1.0", - "math-intrinsics": "^1.0.0", - "object-inspect": "^1.13.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.3", - "safe-regex-test": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.3", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.16" + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, - "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, - "license": "MIT", "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/es-iterator-helpers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", - "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.3", - "safe-array-concat": "^1.1.2" + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, - "license": "MIT", "dependencies": { - "es-errors": "^1.3.0" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "node_modules/jest-leak-detector/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, - "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-latex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==", - "license": "MIT", - "peer": true + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", + "node_modules/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "node_modules/jest-matcher-utils/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, - "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, - "bin": { - "eslint": "bin/eslint.js" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, - "peerDependencies": { - "eslint": ">=7.0.0" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/jest-message-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, - "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-module-utils": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", - "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, "engines": { - "node": ">=4" + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" }, "peerDependenciesMeta": { - "eslint": { + "jest-resolve": { "optional": true } } }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-import": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", - "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, - "license": "MIT", "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.8", - "array.prototype.findlastindex": "^1.2.5", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.0", - "hasown": "^2.0.2", - "is-core-module": "^2.15.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.0", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.8", - "tsconfig-paths": "^3.15.0" + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, - "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "esutils": "^2.0.2" + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "*" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", - "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "aria-query": "^5.3.2", - "array-includes": "^3.1.8", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "^4.10.0", - "axobject-query": "^4.1.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "hasown": "^2.0.2", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "safe-regex-test": "^1.0.3", - "string.prototype.includes": "^2.0.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4.0" + "node": ">=10" }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/jest-snapshot/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": "*" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-prettier": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", - "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.9.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": "*", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint-plugin-react": { - "version": "7.37.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", - "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, - "license": "MIT", "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.1.0", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.8", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", - "string.prototype.repeat": "^1.0.0" + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "esutils": "^2.0.2" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/jest-validate/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" }, "engines": { - "node": "*" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "has-flag": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "dependencies": { + "argparse": "^2.0.1" }, - "funding": { - "url": "https://opencollective.com/eslint" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": "*" + "node": ">=6" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { - "estraverse": "^5.1.0" + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" }, "engines": { - "node": ">=0.10" + "node": ">=4.0" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", "dependencies": { - "estraverse": "^5.2.0" + "tsscmp": "1.0.6" }, "engines": { - "node": ">=4.0" + "node": ">= 0.6" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" + "dependencies": { + "json-buffer": "3.0.1" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, - "license": "BSD-2-Clause", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", - "license": "MIT", + "node_modules/koa": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.11.0.tgz", + "integrity": "sha512-EpR9dElBTDlaDgyhDMiLkXrPwp6ZqgAIBvhhmxQ9XN4TFgW+gEz6tkcsNI6BnUbUftrKDjVFj4lW2/J2aNBMMA==", "dependencies": { - "homedir-polyfill": "^1.0.1" + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.8.0", + "debug": "~3.1.0", + "delegates": "^1.0.0", + "depd": "^1.1.2", + "destroy": "^1.0.4", + "encodeurl": "^1.0.2", + "error-inject": "^1.0.0", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^1.2.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" }, "engines": { - "node": ">=0.10.0" + "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" + "node_modules/koa-compose": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", + "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==" }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "license": "MIT", + "node_modules/koa-convert": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-1.2.0.tgz", + "integrity": "sha512-K9XqjmEDStGX09v3oxR7t5uPRy0jqJdvodHa6wxWTHrTfDq0WUNnYTOOUZN6g8OM8oZQXprQASbiIXG2Ez8ehA==", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "co": "^4.6.0", + "koa-compose": "^3.0.0" }, "engines": { - "node": ">=8.6.0" + "node": ">= 4" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/koa-convert/node_modules/koa-compose": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz", + "integrity": "sha512-8gen2cvKHIZ35eDEik5WOo8zbVp9t4cP8p4hW4uE55waxolLRexKKrqfCpwhGVppnB40jWeF8bZeTVg99eZgPw==", + "dependencies": { + "any-promise": "^1.1.0" + } + }, + "node_modules/koa/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/koa/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, - "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" + "language-subtag-registry": "^0.3.20" }, "engines": { - "node": ">= 6" + "node": ">=0.10" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, - "license": "MIT" + "engines": { + "node": ">=6" + } }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "license": "ISC", "dependencies": { - "reusify": "^1.0.4" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "p-locate": "^5.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.clonedeepwith": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz", + "integrity": "sha512-QRBRSxhbtsX1nc0baxSkkK5WlVTTm/s48DSukcGcWZwIyI8Zz+lB+kFiELJXtzfH4Aj6kMWQ1VWW4U5uUDgZMA==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", "dependencies": { - "to-regex-range": "^5.0.1" + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" }, "engines": { - "node": ">=8" + "node": ">=8.0" } }, - "node_modules/find-file-up": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-2.0.1.tgz", - "integrity": "sha512-qVdaUhYO39zmh28/JLQM5CoYN9byEOKEH4qfa8K1eNV17W0UUMJ9WgbR/hHFH+t5rcl+6RTb5UC7ck/I+uRkpQ==", - "license": "MIT", + "node_modules/long-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", + "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dependencies": { - "resolve-dir": "^1.0.1" + "js-tokens": "^3.0.0 || ^4.0.0" }, - "engines": { - "node": ">=8" + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/find-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-2.0.0.tgz", - "integrity": "sha512-WgZ+nKbELDa6N3i/9nrHeNznm+lY3z4YfhDDWgW+5P0pdmMj26bxaxU11ookgY3NyP9GC7HvZ9etp0jRFqGEeQ==", - "license": "MIT", + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dependencies": { - "find-file-up": "^2.0.1" + "yallist": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "license": "MIT", - "peer": true + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "engines": { + "node": ">=12" + } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, - "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "semver": "^7.5.3" }, "engines": { "node": ">=10" @@ -4251,324 +9396,359 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, - "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "tmpl": "1.0.5" + } + }, + "node_modules/marked": { + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.6.tgz", + "integrity": "sha512-Y07CUOE+HQXbVDCGl3LXggqJDbXDP2pArc2C1N1RRMN0ONiShoSsIInMd5Gsxupe7fKLpgimTV+HOJ9r7bA+pg==", + "bin": { + "marked": "bin/marked.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 18" } }, - "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "license": "MIT", + "node_modules/mathjs": { + "version": "10.6.4", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.6.4.tgz", + "integrity": "sha512-omQyvRE1jIy+3k2qsqkWASOcd45aZguXZDckr3HtnTYyXk5+2xpVfC3kATgbO2Srjxlqww3TVdhD0oUdZ/hiFA==", "dependencies": { - "is-callable": "^1.1.3" + "@babel/runtime": "^7.18.6", + "complex.js": "^2.1.1", + "decimal.js": "^10.3.1", + "escape-latex": "^1.2.0", + "fraction.js": "^4.2.0", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^2.1.0" + }, + "bin": { + "mathjs": "bin/cli.js" + }, + "engines": { + "node": ">= 14" } }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, + "node_modules/mdi-material-ui": { + "version": "7.9.3", + "resolved": "https://registry.npmjs.org/mdi-material-ui/-/mdi-material-ui-7.9.3.tgz", + "integrity": "sha512-l56RUX0tnxY+U1z6hE3cHKXwGUIGpHJLiW2papfvTB1qTegpn3iYU6sX+XBPtl+KG7Ww2RqCLAT03CNzOqeoIg==", + "peerDependencies": { + "@mui/material": "^5.0.0 || ^6.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { - "node": ">= 6" + "node": ">= 0.6" } }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "license": "MIT", - "peer": true, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "engines": { - "node": "*" + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" + "engines": { + "node": ">=8.6" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "license": "MIT", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "mime-db": "1.52.0" }, "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, - "license": "ISC" + "engines": { + "node": ">=6" + } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" } }, - "node_modules/function.prototype.name": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.7.tgz", - "integrity": "sha512-2g4x+HqTJKM9zcJqBSpjoRmdcPFtJM60J3xJisTQSXBWka5XqyBN/2tNUgma1mztTXyDuUsEtYe5qcs7xYzYQA==", + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-intrinsic": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", - "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "dunder-proto": "^1.0.0", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "function-bind": "^1.1.2", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.0.0" - }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.6" } }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dev": true, - "license": "MIT", + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/node-schedule": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz", + "integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==", "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" + "cron-parser": "^4.2.0", + "long-timeout": "0.1.1", + "sorted-array-functions": "^1.3.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", + "node_modules/notistack": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/notistack/-/notistack-2.0.8.tgz", + "integrity": "sha512-/IY14wkFp5qjPgKNvAdfL5Jp6q90+MjgKTPh4c81r/lW70KeuX6b9pE/4f8L4FG31cNudbN9siiFS5ql1aSLRw==", "dependencies": { - "is-glob": "^4.0.3" + "clsx": "^1.1.0", + "hoist-non-react-statics": "^3.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/notistack" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "@mui/material": "^5.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/notistack/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", "engines": { - "node": ">=10.13.0" + "node": ">=6" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", + "node_modules/numbro": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/numbro/-/numbro-2.5.0.tgz", + "integrity": "sha512-xDcctDimhzko/e+y+Q2/8i3qNC9Svw1QgOkSkQoO0kIPI473tR9QRbo2KP88Ty9p8WbPy+3OpTaAIzehtuHq+A==", "dependencies": { - "brace-expansion": "^1.1.7" + "bignumber.js": "^8 || ^9" }, "engines": { "node": "*" } }, - "node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "license": "MIT", - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - }, + "node_modules/nwsapi": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { "node": ">=0.10.0" } }, - "node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", - "license": "MIT", - "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "dev": true, + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "license": "ISC", + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, "dependencies": { - "isexe": "^2.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" }, - "bin": { - "which": "bin/which" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, - "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, - "license": "MIT", "dependencies": { + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "gopd": "^1.0.1" + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -4577,33 +9757,31 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, - "license": "MIT", "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, - "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -4611,59 +9789,72 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } }, - "node_modules/graphemer": { + "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "license": "MIT" + "dependencies": { + "wrappy": "1" + } }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } + "node_modules/only": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", + "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==" }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, - "license": "MIT", "dependencies": { - "dunder-proto": "^1.0.0" + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -4672,590 +9863,579 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "dependencies": { - "has-symbols": "^1.0.3" + "p-limit": "^3.0.2" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "react-is": "^16.7.0" + "node": ">=6" } }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "license": "MIT", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dependencies": { - "parse-passwd": "^1.0.0" + "callsites": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/html-entities": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", - "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ], - "license": "MIT" - }, - "node_modules/http-assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", - "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", - "license": "MIT", + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dependencies": { - "deep-equal": "~1.0.1", - "http-errors": "~1.8.0" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" + "node": ">=8" }, - "engines": { - "node": ">= 0.6" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "engines": { - "node": ">= 4" + "node": ">=0.10.0" } }, - "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "license": "MIT", - "peer": true, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dev": true, + "dependencies": { + "entities": "^4.5.0" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.8.19" + "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT", - "peer": true + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" + "node": ">=8.6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 6" } }, - "node_modules/is-boolean-object": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", - "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" + "find-up": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/is-core-module": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", - "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", - "license": "MIT", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "dependencies": { - "hasown": "^2.0.2" + "p-locate": "^4.1.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" + "p-try": "^2.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, - "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" + "p-limit": "^2.2.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "license": "MIT", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "fast-diff": "^1.1.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.0.0" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, - "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" }, "engines": { - "node": ">=0.10.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, - "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, "engines": { - "node": ">= 0.4" + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "dependencies": { + "punycode": "^2.3.1" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/lupomontero" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": ">=6" } }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, + "node_modules/rambda": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-9.4.1.tgz", + "integrity": "sha512-awZe9AzmPI8XqizJz+NlaRbAdjhWKvuIaPikqRH6r41/ui9UTUQY5jTVdgQwnVrv1HnSMB6IBAAnNYs8HoVvZg==" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "loose-envify": "^1.1.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node_modules/react-colorful": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", + "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" } }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "license": "MIT", + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-error-boundary": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", + "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "@babel/runtime": "^7.12.5" }, "engines": { - "node": ">= 0.4" + "node": ">=10", + "npm": ">=6" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "react": ">=16.13.1" } }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "license": "MIT", + "node_modules/react-hook-form": { + "version": "7.54.2", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz", + "integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==", "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "node_modules/react-is": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==" + }, + "node_modules/react-refresh": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.16.0.tgz", + "integrity": "sha512-FPvF2XxTSikpJxcr+bHut2H4gJ17+18Uy20D5/F+SKzFap62R3cM5wH6b8WN3LyGSYeQilLEcJcR1fjBSI2S1A==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "license": "MIT", + "node_modules/react-router": { + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.1.tgz", + "integrity": "sha512-2omQTA3rkMljmrvvo6WtewGdVh45SpL9hGiCI9uUrwGGfNFDIvGK4gYJsKlJoNVi6AQZcopSCballL+QGOm7fA==", + "peer": true, "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "@remix-run/router": "1.21.0" }, "engines": { - "node": ">= 0.4" + "node": ">=14.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "react": ">=16.8" } }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "license": "MIT", + "node_modules/react-router-dom": { + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.1.tgz", + "integrity": "sha512-YraE27C/RdjcZwl5UCqF/ffXnZDxpJdk9Q6jw38SZHjXs7NNdpViq2l2c7fO7+4uWaEfcwfGCv3RSg4e1By/fQ==", + "peer": true, "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" + "@remix-run/router": "1.21.0", + "react-router": "6.28.1" }, "engines": { - "node": ">= 0.4" + "node": ">=14.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" } }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, - "license": "MIT", + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" } }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", + "node_modules/react-virtuoso": { + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.12.3.tgz", + "integrity": "sha512-6X1p/sU7hecmjDZMAwN+r3go9EVjofKhwkUbVlL8lXhBZecPv9XVCkZ/kBPYOr0Mv0Vl5+Ziwgexg9Kh7+NNXQ==", "engines": { - "node": ">= 0.4" + "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "react": ">=16 || >=17 || >= 18", + "react-dom": ">=16 || >=17 || >= 18" } }, - "node_modules/is-weakref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", - "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bound": "^1.0.2" + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -5264,731 +10444,637 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/isomorphic-ws": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", - "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", - "license": "MIT", - "peerDependencies": { - "ws": "*" - } + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, - "node_modules/iterator.prototype": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.4.tgz", - "integrity": "sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, - "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "reflect.getprototypeof": "^1.0.8", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" - } - }, - "node_modules/javascript-natural-sort": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", - "license": "MIT", - "peer": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "peer": true, - "bin": { - "jsesc": "bin/jsesc" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT", - "peer": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "license": "MIT", + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">=4.0" + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/keygrip": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", - "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", - "license": "MIT", + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, "dependencies": { - "tsscmp": "1.0.6" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" + "engines": { + "node": ">=8" } }, - "node_modules/koa": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.11.0.tgz", - "integrity": "sha512-EpR9dElBTDlaDgyhDMiLkXrPwp6ZqgAIBvhhmxQ9XN4TFgW+gEz6tkcsNI6BnUbUftrKDjVFj4lW2/J2aNBMMA==", - "license": "MIT", + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dependencies": { - "accepts": "^1.3.5", - "cache-content-type": "^1.0.0", - "content-disposition": "~0.5.2", - "content-type": "^1.0.4", - "cookies": "~0.8.0", - "debug": "~3.1.0", - "delegates": "^1.0.0", - "depd": "^1.1.2", - "destroy": "^1.0.4", - "encodeurl": "^1.0.2", - "error-inject": "^1.0.0", - "escape-html": "^1.0.3", - "fresh": "~0.5.2", - "http-assert": "^1.3.0", - "http-errors": "^1.6.3", - "is-generator-function": "^1.0.7", - "koa-compose": "^4.1.0", - "koa-convert": "^1.2.0", - "on-finished": "^2.3.0", - "only": "~0.0.2", - "parseurl": "^1.3.2", - "statuses": "^1.5.0", - "type-is": "^1.6.16", - "vary": "^1.1.2" + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" }, "engines": { - "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" + "node": ">=0.10.0" } }, - "node_modules/koa-compose": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", - "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", - "license": "MIT" - }, - "node_modules/koa-convert": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-1.2.0.tgz", - "integrity": "sha512-K9XqjmEDStGX09v3oxR7t5uPRy0jqJdvodHa6wxWTHrTfDq0WUNnYTOOUZN6g8OM8oZQXprQASbiIXG2Ez8ehA==", - "license": "MIT", - "dependencies": { - "co": "^4.6.0", - "koa-compose": "^3.0.0" - }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "engines": { - "node": ">= 4" + "node": ">=4" } }, - "node_modules/koa-convert/node_modules/koa-compose": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz", - "integrity": "sha512-8gen2cvKHIZ35eDEik5WOo8zbVp9t4cP8p4hW4uE55waxolLRexKKrqfCpwhGVppnB40jWeF8bZeTVg99eZgPw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.1.0" + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "engines": { + "node": ">=10" } }, - "node_modules/koa/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/koa/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "CC0-1.0" + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "language-subtag-registry": "^0.3.20" - }, - "engines": { - "node": ">=0.10" + "queue-microtask": "^1.2.2" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, - "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">= 0.8.0" + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT", - "peer": true + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, - "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "es-errors": "^1.3.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT", - "peer": true - }, - "node_modules/lodash.clonedeepwith": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz", - "integrity": "sha512-QRBRSxhbtsX1nc0baxSkkK5WlVTTm/s48DSukcGcWZwIyI8Zz+lB+kFiELJXtzfH4Aj6kMWQ1VWW4U5uUDgZMA==", - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/log4js": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", - "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", - "license": "Apache-2.0", + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "flatted": "^3.2.7", - "rfdc": "^1.3.0", - "streamroller": "^3.1.5" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" }, "engines": { - "node": ">=8.0" - } - }, - "node_modules/long-timeout": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", - "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==", - "license": "MIT" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "node": ">= 0.4" }, - "bin": { - "loose-envify": "cli.js" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lru-cache": { + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/saxes": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "xmlchars": "^2.2.0" }, "engines": { - "node": ">=10" + "node": ">=v12.22.7" } }, - "node_modules/luxon": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", - "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", - "license": "MIT", - "engines": { - "node": ">=12" + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" } }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "license": "MIT", + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, "bin": { - "marked": "bin/marked.js" + "semver": "bin/semver.js" }, "engines": { - "node": ">= 12" + "node": ">=10" } }, - "node_modules/math-intrinsics": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.0.0.tgz", - "integrity": "sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==", + "node_modules/serialize-query-params": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/serialize-query-params/-/serialize-query-params-2.0.2.tgz", + "integrity": "sha512-1chMo1dST4pFA9RDXAtF0Rbjaut4is7bzFbI1Z26IuMub68pNCILku85aYmeFhvnY//BXUPUhoRMjYcsT93J/Q==" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, - "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, "engines": { "node": ">= 0.4" } }, - "node_modules/mathjs": { - "version": "10.6.4", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.6.4.tgz", - "integrity": "sha512-omQyvRE1jIy+3k2qsqkWASOcd45aZguXZDckr3HtnTYyXk5+2xpVfC3kATgbO2Srjxlqww3TVdhD0oUdZ/hiFA==", - "license": "Apache-2.0", - "peer": true, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.18.6", - "complex.js": "^2.1.1", - "decimal.js": "^10.3.1", - "escape-latex": "^1.2.0", - "fraction.js": "^4.2.0", - "javascript-natural-sort": "^0.7.1", - "seedrandom": "^3.0.5", - "tiny-emitter": "^2.1.0", - "typed-function": "^2.1.0" - }, - "bin": { - "mathjs": "bin/cli.js" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/mdi-material-ui": { - "version": "7.9.3", - "resolved": "https://registry.npmjs.org/mdi-material-ui/-/mdi-material-ui-7.9.3.tgz", - "integrity": "sha512-l56RUX0tnxY+U1z6hE3cHKXwGUIGpHJLiW2papfvTB1qTegpn3iYU6sX+XBPtl+KG7Ww2RqCLAT03CNzOqeoIg==", - "license": "MIT", - "peer": true, - "peerDependencies": { - "@mui/material": "^5.0.0 || ^6.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node": ">= 0.4" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, - "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">= 8" + "node": ">= 0.4" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=8.6" + "node": ">=8" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, "dependencies": { - "mime-db": "1.52.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.4" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-schedule": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz", - "integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==", - "license": "MIT", "dependencies": { - "cron-parser": "^4.2.0", - "long-timeout": "0.1.1", - "sorted-array-functions": "^1.3.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/notistack": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/notistack/-/notistack-2.0.8.tgz", - "integrity": "sha512-/IY14wkFp5qjPgKNvAdfL5Jp6q90+MjgKTPh4c81r/lW70KeuX6b9pE/4f8L4FG31cNudbN9siiFS5ql1aSLRw==", - "license": "MIT", - "peer": true, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, "dependencies": { - "clsx": "^1.1.0", - "hoist-non-react-statics": "^3.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/notistack" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, - "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "@mui/material": "^5.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "engines": { + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/notistack/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "license": "MIT", - "peer": true, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/numbro": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/numbro/-/numbro-2.5.0.tgz", - "integrity": "sha512-xDcctDimhzko/e+y+Q2/8i3qNC9Svw1QgOkSkQoO0kIPI473tR9QRbo2KP88Ty9p8WbPy+3OpTaAIzehtuHq+A==", - "license": "MIT", + "node_modules/sorted-array-functions": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==" + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "peer": true, - "dependencies": { - "bignumber.js": "^8 || ^9" - }, "engines": { - "node": "*" + "node": ">=0.10.0" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "escape-string-regexp": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=10" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, - "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, - "license": "MIT", + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "dev": true + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8.0" + } + }, + "node_modules/streamroller/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6 <7 || >=8" } }, - "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "node_modules/streamroller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/streamroller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" } }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" + "es-abstract": "^1.23.3" }, "engines": { "node": ">= 0.4" } }, - "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5997,616 +11083,512 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/only": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", - "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==" - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, - "license": "MIT", "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, - "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, - "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "license": "MIT", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, "dependencies": { - "callsites": "^3.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "license": "MIT", - "peer": true, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" } }, - "node_modules/path-exists": { + "node_modules/strip-bom": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, - "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "license": "MIT", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC", + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", "peer": true }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=8" } }, - "node_modules/possible-typed-array-names": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "engines": { "node": ">= 0.4" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, - "license": "MIT", "dependencies": { - "fast-diff": "^1.1.2" + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/rambda": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/rambda/-/rambda-9.4.1.tgz", - "integrity": "sha512-awZe9AzmPI8XqizJz+NlaRbAdjhWKvuIaPikqRH6r41/ui9UTUQY5jTVdgQwnVrv1HnSMB6IBAAnNYs8HoVvZg==", - "license": "MIT" - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "peer": true, "dependencies": { - "loose-envify": "^1.1.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/react-colorful": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", - "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", - "license": "MIT", - "peer": true, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "brace-expansion": "^1.1.7" }, - "peerDependencies": { - "react": "^18.3.1" + "engines": { + "node": "*" } }, - "node_modules/react-error-boundary": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", - "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", - "license": "MIT", - "peer": true, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.12.5" + "is-number": "^7.0.0" }, "engines": { - "node": ">=10", - "npm": ">=6" - }, - "peerDependencies": { - "react": ">=16.13.1" + "node": ">=8.0" } }, - "node_modules/react-hook-form": { - "version": "7.54.1", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.1.tgz", - "integrity": "sha512-PUNzFwQeQ5oHiiTUO7GO/EJXGEtuun2Y1A59rLnZBBj+vNEOWt/3ERTiG1/zt7dVeJEM+4vDX/7XQ/qanuvPMg==", - "license": "MIT", - "peer": true, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "engines": { - "node": ">=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-hook-form" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18 || ^19" + "node": ">=0.6" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } }, - "node_modules/react-refresh": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.16.0.tgz", - "integrity": "sha512-FPvF2XxTSikpJxcr+bHut2H4gJ17+18Uy20D5/F+SKzFap62R3cM5wH6b8WN3LyGSYeQilLEcJcR1fjBSI2S1A==", + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 4.0.0" } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "peer": true, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" + "punycode": "^2.1.1" }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" + "engines": { + "node": ">=12" } }, - "node_modules/react-virtuoso": { - "version": "4.12.3", - "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.12.3.tgz", - "integrity": "sha512-6X1p/sU7hecmjDZMAwN+r3go9EVjofKhwkUbVlL8lXhBZecPv9XVCkZ/kBPYOr0Mv0Vl5+Ziwgexg9Kh7+NNXQ==", - "license": "MIT", - "peer": true, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=16" }, "peerDependencies": { - "react": ">=16 || >=17 || >= 18", - "react-dom": ">=16 || >=17 || >= 18" + "typescript": ">=4.2.0" } }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz", - "integrity": "sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==", + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "dunder-proto": "^1.0.0", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.2.0", - "which-builtin-type": "^1.2.0" + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" }, - "engines": { - "node": ">= 0.4" + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT", - "peer": true - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", - "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" } }, - "node_modules/resolve": { - "version": "1.22.9", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz", - "integrity": "sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==", - "license": "MIT", + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "minimist": "^1.2.0" }, "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "json5": "lib/cli.js" } }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "license": "MIT", + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "engines": { + "node": ">=0.6.x" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "license": "MIT", + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, "engines": { "node": ">=4" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "license": "MIT", "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "license": "MIT" + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, - "license": "ISC", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 0.4" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", "dependencies": { - "queue-microtask": "^1.2.2" + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, - "license": "MIT", "dependencies": { + "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { - "node": ">=0.4" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -6615,337 +11597,307 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" + "node_modules/typed-function": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", + "integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==", + "engines": { + "node": ">= 10" } }, - "node_modules/seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", - "license": "MIT", - "peer": true - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "bin": { - "semver": "bin/semver.js" + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=10" + "node": ">=14.17" } }, - "node_modules/serialize-query-params": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/serialize-query-params/-/serialize-query-params-2.0.2.tgz", - "integrity": "sha512-1chMo1dST4pFA9RDXAtF0Rbjaut4is7bzFbI1Z26IuMub68pNCILku85aYmeFhvnY//BXUPUhoRMjYcsT93J/Q==", - "license": "ISC", - "peer": true - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, - "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "engines": { - "node": ">= 0.4" + "node": ">= 10.0.0" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" + "node_modules/upath": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", + "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", + "engines": { + "node": ">=4", + "yarn": "*" + } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "shebang-regex": "^3.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, - "engines": { - "node": ">=8" + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "punycode": "^2.1.0" } }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, - "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" } }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "license": "MIT", + "node_modules/use-immer": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/use-immer/-/use-immer-0.7.0.tgz", + "integrity": "sha512-Re4hjrP3a/2ABZjAc0b7AK9s626bnO+H33RO2VUhiDZ2StBz5B663K6WNNlr4QtHWaGUmvLpwt3whFvvWuolQw==", + "peerDependencies": { + "immer": ">=2.0.0", + "react": "^16.8.0 || ^17.0.1 || ^18.0.0" + } + }, + "node_modules/use-query-params": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/use-query-params/-/use-query-params-2.2.1.tgz", + "integrity": "sha512-i6alcyLB8w9i3ZK3caNftdb+UnbfBRNPDnc89CNQWkGRmDrm/gfydHvMBfVsQJRq3NoHOM2dt/ceBWG2397v1Q==", "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "serialize-query-params": "^2.0.2" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "@reach/router": "^1.2.1", + "react": ">=16.8.0", + "react-dom": ">=16.8.0", + "react-router-dom": ">=5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "@reach/router": { + "optional": true + }, + "react-router-dom": { + "optional": true + } } }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "license": "MIT", + "node_modules/use-resize-observer": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", + "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", + "peer": true, "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" + "@juggle/resize-observer": "^3.3.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "react": "16.8.0 - 18", + "react-dom": "16.8.0 - 18" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "peer": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10.12.0" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/sorted-array-functions": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", - "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==", - "license": "MIT" + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "license": "BSD-3-Clause", - "peer": true, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=14" } }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, - "license": "MIT" + "dependencies": { + "makeerror": "1.0.12" + } }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "license": "MIT", + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=12" } }, - "node_modules/streamroller": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", - "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", - "license": "MIT", - "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "fs-extra": "^8.1.0" - }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, "engines": { - "node": ">=8.0" + "node": ">=10.13.0" } }, - "node_modules/streamroller/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "license": "MIT", + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "iconv-lite": "0.6.3" }, "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/streamroller/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "node": ">=12" } }, - "node_modules/streamroller/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "license": "MIT", + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, "engines": { - "node": ">= 4.0.0" + "node": ">=12" } }, - "node_modules/string.prototype.includes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", - "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3" + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" + "isexe": "^2.0.0" }, - "engines": { - "node": ">= 0.4" + "bin": { + "node-which": "bin/node-which" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "engines": { + "node": ">= 8" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -6954,17 +11906,25 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -6973,16 +11933,16 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -6991,73 +11951,19 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/which-typed-array": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-mod": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", - "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", - "license": "MIT", - "peer": true - }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", - "license": "MIT", - "peer": true - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7065,575 +11971,673 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/synckit": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", - "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "MIT", "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", - "license": "MIT", - "peer": true + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, - "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" }, "engines": { - "node": ">=8.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", + "node_modules/ws": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", "engines": { - "node": ">=0.6" + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", "dev": true, - "license": "MIT", "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" + "node": ">=12" } }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" + "engines": { + "node": ">=10" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "node_modules/tsscmp": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", - "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", - "license": "MIT", + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "peer": true, "engines": { - "node": ">=0.6.x" + "node": ">= 6" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=12" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, + "node_modules/ylru": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz", + "integrity": "sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==", "engines": { - "node": ">= 0.6" + "node": ">= 4.0.0" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, "engines": { - "node": ">= 0.4" + "node": ">=6" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz", - "integrity": "sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, - "license": "MIT", + "node_modules/zrender": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz", + "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "tslib": "2.3.0" } }, - "node_modules/typed-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", - "integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==", - "peer": true, - "engines": { - "node": ">= 10" + "node_modules/zrender/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, + "PieChart": { + "name": "@perses-dev/pie-chart", + "version": "0.4.0", + "dependencies": { + "@module-federation/enhanced": "^0.1.11" + }, + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "@perses-dev/components": "^0.49.0", + "@perses-dev/core": "^0.49.0", + "@perses-dev/plugin-system": "^0.49.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "immer": "^9.0.15", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0" } }, - "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "Prometheus": { + "name": "@perses-dev/prometheus", + "version": "0.4.0", + "dependencies": { + "@module-federation/enhanced": "^0.1.11", + "@prometheus-io/codemirror-promql": "^0.43.0", + "color-hash": "^2.0.2" }, - "engines": { - "node": ">=14.17" + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "@perses-dev/components": "^0.49.0", + "@perses-dev/core": "^0.49.0", + "@perses-dev/plugin-system": "^0.49.0", + "@tanstack/react-query": "^4.36.1", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "immer": "^9.0.15", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "react-hook-form": "^7.52.2", + "use-resize-observer": "^9.0.0" } }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "dev": true, - "license": "MIT", + "ScatterChart": { + "name": "@perses-dev/scatter-chart", + "version": "0.4.0", "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" + "@module-federation/enhanced": "^0.1.11" }, - "engines": { - "node": ">= 0.4" + "devDependencies": { + "react-virtuoso": "^4.12.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "@perses-dev/components": "^0.49.0", + "@perses-dev/core": "^0.49.0", + "@perses-dev/plugin-system": "^0.49.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0" } }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" + "StatChart": { + "name": "@perses-dev/stat-chart", + "version": "0.4.0", + "dependencies": { + "@module-federation/enhanced": "^0.1.11" + }, + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "@perses-dev/components": "^0.49.0", + "@perses-dev/core": "^0.49.0", + "@perses-dev/plugin-system": "^0.49.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "immer": "^9.0.15", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0" } }, - "node_modules/upath": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", - "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", - "license": "MIT", - "engines": { - "node": ">=4", - "yarn": "*" + "StaticListVariable": { + "name": "@perses-dev/static-list-variable", + "version": "0.1.0", + "dependencies": { + "@module-federation/enhanced": "^0.1.11", + "color-hash": "^2.0.2" + }, + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "@perses-dev/components": "^0.49.0", + "@perses-dev/core": "^0.49.0", + "@perses-dev/plugin-system": "^0.49.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", + "StatusHistoryChart": { + "name": "@perses-dev/status-history-chart", + "version": "0.4.0", "dependencies": { - "punycode": "^2.1.0" + "@module-federation/enhanced": "^0.1.11", + "@perses-dev/components": "^0.50.0-rc.1", + "@perses-dev/core": "^0.50.0-rc.1", + "@perses-dev/plugin-system": "^0.50.0-rc.1" + }, + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "immer": "^9.0.15", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0" } }, - "node_modules/use-immer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/use-immer/-/use-immer-0.7.0.tgz", - "integrity": "sha512-Re4hjrP3a/2ABZjAc0b7AK9s626bnO+H33RO2VUhiDZ2StBz5B663K6WNNlr4QtHWaGUmvLpwt3whFvvWuolQw==", - "license": "MIT", + "StatusHistoryChart/node_modules/@mui/core-downloads-tracker": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.0.tgz", + "integrity": "sha512-6u74wi+9zeNlukrCtYYET8Ed/n9AS27DiaXCZKAD3TRGFaqiyYSsQgN2disW83pI/cM1Q2lJY1JX4YfwvNtlNw==", "peer": true, - "peerDependencies": { - "immer": ">=2.0.0", - "react": "^16.8.0 || ^17.0.1 || ^18.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" } }, - "node_modules/use-query-params": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/use-query-params/-/use-query-params-2.2.1.tgz", - "integrity": "sha512-i6alcyLB8w9i3ZK3caNftdb+UnbfBRNPDnc89CNQWkGRmDrm/gfydHvMBfVsQJRq3NoHOM2dt/ceBWG2397v1Q==", - "license": "ISC", + "StatusHistoryChart/node_modules/@mui/material": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.0.tgz", + "integrity": "sha512-hNIgwdM9U3DNmowZ8mU59oFmWoDKjc92FqQnQva3Pxh6xRKWtD2Ej7POUHMX8Dwr1OpcSUlT2+tEMeLb7WYsIg==", "peer": true, "dependencies": { - "serialize-query-params": "^2.0.2" + "@babel/runtime": "^7.26.0", + "@mui/core-downloads-tracker": "^6.4.0", + "@mui/system": "^6.4.0", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.0", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@reach/router": "^1.2.1", - "react": ">=16.8.0", - "react-dom": ">=16.8.0", - "react-router-dom": ">=5" + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^6.4.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { - "@reach/router": { + "@emotion/react": { "optional": true }, - "react-router-dom": { + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { "optional": true } } }, - "node_modules/use-resize-observer": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", - "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", - "license": "MIT", + "StatusHistoryChart/node_modules/@mui/utils": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.0.tgz", + "integrity": "sha512-woOTATWNsTNR3YBh2Ixkj3l5RaxSiGoC9G8gOpYoFw1mZM77LWJeuMHFax7iIW4ahK0Cr35TF9DKtrafJmOmNQ==", "peer": true, "dependencies": { - "@juggle/resize-observer": "^3.3.1" + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.21", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" }, - "peerDependencies": { - "react": "16.8.0 - 18", - "react-dom": "16.8.0 - 18" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", - "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", - "license": "MIT", - "peer": true, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", "engines": { - "node": ">= 0.8" - } - }, - "node_modules/w3c-keyname": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", - "license": "MIT", - "peer": true - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" + "node": ">=14.0.0" }, - "bin": { - "node-which": "bin/node-which" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" }, - "engines": { - "node": ">= 8" + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "dev": true, - "license": "MIT", + "StatusHistoryChart/node_modules/@perses-dev/components": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@perses-dev/components/-/components-0.50.0.tgz", + "integrity": "sha512-mi+0bNOZwzzZ9r9hgHvOYqpCk/WqZLAbCyI/U9oJ1b/WnF4zTMiZVjy3goyk6YufrUZY5rDWjnUYkUjCwJii6g==", "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" + "@atlaskit/pragmatic-drag-and-drop": "^1.4.0", + "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3", + "@codemirror/lang-json": "^6.0.1", + "@fontsource/lato": "^4.5.10", + "@mui/x-date-pickers": "^7.23.1", + "@perses-dev/core": "0.50.0", + "@tanstack/react-table": "^8.20.5", + "@uiw/react-codemirror": "^4.19.1", + "date-fns": "^2.28.0", + "date-fns-tz": "^1.3.7", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "mathjs": "^10.6.4", + "mdi-material-ui": "^7.9.2", + "notistack": "^2.0.5", + "react-colorful": "^5.6.1", + "react-error-boundary": "^3.1.4", + "react-hook-form": "^7.51.3", + "react-virtuoso": "^4.12.2", + "zod": "^3.21.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@mui/material": "^6.1.10", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" } }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "dev": true, - "license": "MIT", + "StatusHistoryChart/node_modules/@perses-dev/core": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@perses-dev/core/-/core-0.50.0.tgz", + "integrity": "sha512-dUrCJmfEs6UirizSnEHhrFbR9QNxBFRa7WoSCsjyXRaCEQLEf0cDnxvLQrlygFPpTQsZjHMB9DCiEvgq83Vj+Q==", "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" + "date-fns": "^2.28.0", + "lodash": "^4.17.21", + "mathjs": "^10.6.4", + "numbro": "^2.3.6", + "zod": "^3.21.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" } }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "license": "MIT", + "StatusHistoryChart/node_modules/@perses-dev/plugin-system": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@perses-dev/plugin-system/-/plugin-system-0.50.0.tgz", + "integrity": "sha512-Ja9MC/wERy5i661iXLisgEbvLSLXWJWRPIRfWdLNmR5E1SYusoz7VNXLw43KzzAOOKJECPGRjTrhNhXO4gMSBA==", "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" + "@perses-dev/components": "0.50.0", + "@perses-dev/core": "0.50.0", + "date-fns": "^2.30.0", + "immer": "^9.0.15", + "react-hook-form": "^7.46.1", + "use-immer": "^0.7.0", + "use-query-params": "^2.1.2", + "zod": "^3.22.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@mui/material": "^6.1.10", + "@tanstack/react-query": "^4.7.1", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" } }, - "node_modules/which-typed-array": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz", - "integrity": "sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==", - "dev": true, - "license": "MIT", + "Table": { + "name": "@perses-dev/table", + "version": "0.4.0", "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" + "@module-federation/enhanced": "^0.1.11", + "@perses-dev/components": "^0.50.0-rc.1", + "@perses-dev/core": "^0.50.0-rc.1", + "@perses-dev/plugin-system": "^0.50.0-rc.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@hookform/resolvers": "^3.2.0", + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.8", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "use-resize-observer": "^9.0.0" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "Table/node_modules/@mui/core-downloads-tracker": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.0.tgz", + "integrity": "sha512-6u74wi+9zeNlukrCtYYET8Ed/n9AS27DiaXCZKAD3TRGFaqiyYSsQgN2disW83pI/cM1Q2lJY1JX4YfwvNtlNw==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", - "license": "MIT", + "Table/node_modules/@mui/material": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.0.tgz", + "integrity": "sha512-hNIgwdM9U3DNmowZ8mU59oFmWoDKjc92FqQnQva3Pxh6xRKWtD2Ej7POUHMX8Dwr1OpcSUlT2+tEMeLb7WYsIg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/core-downloads-tracker": "^6.4.0", + "@mui/system": "^6.4.0", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.0", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" + }, "engines": { - "node": ">=10.0.0" + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^6.4.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { - "bufferutil": { + "@emotion/react": { "optional": true }, - "utf-8-validate": { + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { "optional": true } } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "license": "ISC", + "Table/node_modules/@mui/utils": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.0.tgz", + "integrity": "sha512-woOTATWNsTNR3YBh2Ixkj3l5RaxSiGoC9G8gOpYoFw1mZM77LWJeuMHFax7iIW4ahK0Cr35TF9DKtrafJmOmNQ==", "peer": true, + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.21", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, "engines": { - "node": ">= 6" - } - }, - "node_modules/ylru": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz", - "integrity": "sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==", - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "node": ">=14.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/zod": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", - "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "Table/node_modules/@perses-dev/components": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@perses-dev/components/-/components-0.50.0.tgz", + "integrity": "sha512-mi+0bNOZwzzZ9r9hgHvOYqpCk/WqZLAbCyI/U9oJ1b/WnF4zTMiZVjy3goyk6YufrUZY5rDWjnUYkUjCwJii6g==", + "dependencies": { + "@atlaskit/pragmatic-drag-and-drop": "^1.4.0", + "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3", + "@codemirror/lang-json": "^6.0.1", + "@fontsource/lato": "^4.5.10", + "@mui/x-date-pickers": "^7.23.1", + "@perses-dev/core": "0.50.0", + "@tanstack/react-table": "^8.20.5", + "@uiw/react-codemirror": "^4.19.1", + "date-fns": "^2.28.0", + "date-fns-tz": "^1.3.7", + "echarts": "5.5.0", + "lodash": "^4.17.21", + "mathjs": "^10.6.4", + "mdi-material-ui": "^7.9.2", + "notistack": "^2.0.5", + "react-colorful": "^5.6.1", + "react-error-boundary": "^3.1.4", + "react-hook-form": "^7.51.3", + "react-virtuoso": "^4.12.2", + "zod": "^3.21.4" + }, + "peerDependencies": { + "@mui/material": "^6.1.10", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" } }, - "node_modules/zrender": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz", - "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", - "license": "BSD-3-Clause", - "peer": true, + "Table/node_modules/@perses-dev/core": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@perses-dev/core/-/core-0.50.0.tgz", + "integrity": "sha512-dUrCJmfEs6UirizSnEHhrFbR9QNxBFRa7WoSCsjyXRaCEQLEf0cDnxvLQrlygFPpTQsZjHMB9DCiEvgq83Vj+Q==", "dependencies": { - "tslib": "2.3.0" + "date-fns": "^2.28.0", + "lodash": "^4.17.21", + "mathjs": "^10.6.4", + "numbro": "^2.3.6", + "zod": "^3.21.4" + }, + "peerDependencies": { + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" } }, - "node_modules/zrender/node_modules/tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", - "license": "0BSD", - "peer": true + "Table/node_modules/@perses-dev/plugin-system": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@perses-dev/plugin-system/-/plugin-system-0.50.0.tgz", + "integrity": "sha512-Ja9MC/wERy5i661iXLisgEbvLSLXWJWRPIRfWdLNmR5E1SYusoz7VNXLw43KzzAOOKJECPGRjTrhNhXO4gMSBA==", + "dependencies": { + "@perses-dev/components": "0.50.0", + "@perses-dev/core": "0.50.0", + "date-fns": "^2.30.0", + "immer": "^9.0.15", + "react-hook-form": "^7.46.1", + "use-immer": "^0.7.0", + "use-query-params": "^2.1.2", + "zod": "^3.22.2" + }, + "peerDependencies": { + "@mui/material": "^6.1.10", + "@tanstack/react-query": "^4.7.1", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" + } }, - "Prometheus": { - "name": "@perses-dev/prometheus", - "version": "0.2.0", + "TimeSeriesChart": { + "name": "@perses-dev/timeseries-chart", + "version": "0.4.0", "dependencies": { "@module-federation/enhanced": "^0.1.11", - "@prometheus-io/codemirror-promql": "^0.43.0", "color-hash": "^2.0.2" }, "peerDependencies": { @@ -7646,15 +12650,16 @@ "date-fns": "^2.29.3", "date-fns-tz": "^1.3.8", "echarts": "5.5.0", + "immer": "^9.0.15", "lodash": "^4.17.21", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0", "use-resize-observer": "^9.0.0" } }, - "StatChart": { - "name": "@perses-dev/stat-chart", - "version": "0.3.0", + "TimeSeriesTable": { + "name": "@perses-dev/time-series-table", + "version": "0.4.0", "dependencies": { "@module-federation/enhanced": "^0.1.11" }, @@ -7674,12 +12679,12 @@ "use-resize-observer": "^9.0.0" } }, - "StaticListVariable": { - "name": "@perses-dev/static-list-variable", - "version": "0.1.0", + "TraceTable": { + "name": "@perses-dev/trace-table", + "version": "0.4.0", "dependencies": { "@module-federation/enhanced": "^0.1.11", - "color-hash": "^2.0.2" + "@mui/x-data-grid": "^7.23.1" }, "peerDependencies": { "@emotion/react": "^11.7.1", @@ -7694,12 +12699,13 @@ "lodash": "^4.17.21", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0", + "react-router-dom": "^6.11.0", "use-resize-observer": "^9.0.0" } }, - "TimeSeriesChart": { - "name": "@perses-dev/timeseries-chart", - "version": "0.3.0", + "TracingGanttChart": { + "name": "@perses-dev/tracing-gantt-chart", + "version": "0.4.0", "dependencies": { "@module-federation/enhanced": "^0.1.11", "color-hash": "^2.0.2" diff --git a/package.json b/package.json index d66074f..e034671 100644 --- a/package.json +++ b/package.json @@ -6,24 +6,42 @@ "scripts": { "dev": "npm run dev --workspaces", "build": "npm run build --workspaces", - "lint": "npm run lint --workspaces" + "lint": "npm run lint --workspaces", + "test": "npm run test --workspaces", + "type-check": "npm run type-check --workspaces" }, "workspaces": [ "BarChart", "GaugeChart", "MarkdownChart", + "PieChart", + "Prometheus", + "ScatterChart", "StatChart", + "StaticListVariable", + "StatusHistoryChart", + "Table", "TimeSeriesChart", - "Prometheus", - "StaticListVariable" + "TimeSeriesTable", + "TraceTable", + "TracingGanttChart" ], "devDependencies": { "@rsbuild/core": "^1.1.10", "@rsbuild/plugin-react": "^1.1.0", + "@rspack/core": "^0.6.5", + "@swc/core": "^1.7.10", + "@swc/jest": "^0.2.37", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.1.0", + "@testing-library/user-event": "^13.5.0", "@types/color-hash": "^2.0.0", + "@types/jest": "^29.5.14", "@types/lodash": "^4.17.5", "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.8.0", + "cross-env": "^7.0.3", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", @@ -31,6 +49,9 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.2", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "ts-node": "^10.9.2", "typescript": "^5.4.2" } } diff --git a/scripts/build-archive/build-archive.go b/scripts/build-archive/build-archive.go index 2f84a4d..e186cf4 100644 --- a/scripts/build-archive/build-archive.go +++ b/scripts/build-archive/build-archive.go @@ -15,6 +15,7 @@ package main import ( "fmt" + "os" "os/exec" "path" @@ -30,7 +31,7 @@ var pluginFiles = []string{ "README.md", } -func createArchive(pluginName string) error { +func createArchive(pluginName string, createGroupArchive bool) error { manifest, err := npm.ReadManifest(pluginName) if err != nil { return err @@ -44,21 +45,31 @@ func createArchive(pluginName string) error { // Then let's create the archive with the folder previously created archiveName := fmt.Sprintf("%s-%s.tar.gz", manifest.ID, manifest.Metadata.BuildInfo.Version) - if execErr := exec.Command("tar", "-czvf", path.Join(pluginName, archiveName), "-C", pluginName, pluginName).Run(); execErr != nil { + if execErr := exec.Command("tar", "-czvf", path.Join(pluginName, archiveName), "--disable-copyfile", "-C", pluginName, pluginName).Run(); execErr != nil { return execErr } + if createGroupArchive { + if execErr := exec.Command("cp", path.Join(pluginName, archiveName), path.Join("./plugins-archive", archiveName)).Run(); execErr != nil { + return execErr + } + } + return exec.Command("rm", "-rf", newArchiveFolder).Run() } func main() { + createGroupArchive := false + if len(os.Args) > 1 && os.Args[1] == "--group" { + createGroupArchive = true + } workspaces, err := npm.GetWorkspaces() if err != nil { logrus.WithError(err).Fatal("unable to get the list of the workspaces") } for _, workspace := range workspaces { logrus.Infof("building archive for the plugin %s", workspace) - if createArchiveErr := createArchive(workspace); createArchiveErr != nil { + if createArchiveErr := createArchive(workspace, createGroupArchive); createArchiveErr != nil { logrus.WithError(createArchiveErr).Fatalf("unable to generate the archive for the plugin %s", workspace) } } diff --git a/stylesMock.js b/stylesMock.js new file mode 100644 index 0000000..ccaee66 --- /dev/null +++ b/stylesMock.js @@ -0,0 +1,14 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +module.exports = {}; diff --git a/tsconfig.base.json b/tsconfig.base.json index e6b9bdf..00793d0 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -10,6 +10,5 @@ "resolveJsonModule": true, "moduleResolution": "bundler", "useDefineForClassFields": true - }, - "include": ["src"] + } }