From b11002a1887cc1f0b8a63828821cd04c8c4a4073 Mon Sep 17 00:00:00 2001 From: Seyed Mahmoud Shahrokni <39291137+shahrokni@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:12:01 +0200 Subject: [PATCH 01/17] [BUGFIX] add double backslash behind special charachters when uisng regex inetrpolation (#191) Signed-off-by: Seyed Mahmoud SHAHROKNI --- components/src/utils/variable-interpolation.test.ts | 8 ++++++++ components/src/utils/variable-interpolation.ts | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/components/src/utils/variable-interpolation.test.ts b/components/src/utils/variable-interpolation.test.ts index 57a8a03e..376a2ad2 100644 --- a/components/src/utils/variable-interpolation.test.ts +++ b/components/src/utils/variable-interpolation.test.ts @@ -215,6 +215,14 @@ describe('replaceVariables() with custom formats', () => { }, expected: 'hello (perses\\.|prometheus\\$) (world\\.)', }, + { + text: 'hello ${var1:regexliteral} ${var2:regexliteral}', + state: { + var1: { value: ['perses.', 'prometheus$'], loading: false }, + var2: { value: 'world.', loading: false }, + }, + expected: 'hello (perses\\\\.|prometheus\\\\$) (world\\\\.)', + }, // singlequote { text: 'hello ${var1:singlequote} ${var2:singlequote}', diff --git a/components/src/utils/variable-interpolation.ts b/components/src/utils/variable-interpolation.ts index f536aa88..35d243e4 100644 --- a/components/src/utils/variable-interpolation.ts +++ b/components/src/utils/variable-interpolation.ts @@ -62,6 +62,7 @@ export enum InterpolationFormat { SQLSTRING = 'sqlstring', TEXT = 'text', QUERYPARAM = 'queryparam', + REGEX_LITERAL = 'regexliteral', } function stringToFormat(val: string | undefined): InterpolationFormat | undefined { @@ -110,6 +111,10 @@ export function interpolate(values: string[], name: string, format: Interpolatio const escapedRegex = values.map((v) => v.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')); return `(${escapedRegex.join('|')})`; } + case InterpolationFormat.REGEX_LITERAL: { + const escapedRegex = values.map((v) => v.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\\\$&')); + return `(${escapedRegex.join('|')})`; + } case InterpolationFormat.SINGLEQUOTE: return values.map((v) => `'${v}'`).join(','); case InterpolationFormat.SQLSTRING: @@ -143,7 +148,6 @@ export function replaceVariable( if (typeof variableValue === 'string') { replaceString = interpolate([variableValue], varName, varFormat || InterpolationFormat.RAW); } - text = text.replaceAll(variableSyntax, replaceString); return text.replaceAll(alternativeVariableSyntax, replaceString); } From f7d2333b7f4bbb12d242a03f8ae7f43f9503e6da Mon Sep 17 00:00:00 2001 From: Seyed Mahmoud Shahrokni <39291137+shahrokni@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:35:18 +0200 Subject: [PATCH 02/17] Prepare release v0.54.0-beta.11 (#192) Signed-off-by: Seyed Mahmoud SHAHROKNI --- client/package.json | 2 +- components/package.json | 4 ++-- dashboards/package.json | 8 ++++---- explore/package.json | 8 ++++---- package-lock.json | 32 ++++++++++++++++---------------- package.json | 2 +- plugin-system/package.json | 6 +++--- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/client/package.json b/client/package.json index 0b1a35fc..10c0c8ca 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/client", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "description": "Functions as an API client or Data fetching Layer for interacting with a backend service", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", diff --git a/components/package.json b/components/package.json index 5b771f31..ca69cb5c 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/components", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "description": "Common UI components used across Perses features", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -35,7 +35,7 @@ "@fontsource/inter": "^5.0.0", "@mui/x-date-pickers": "^7.23.1", "@perses-dev/spec": "0.2.0-beta.6", - "@perses-dev/client": "0.54.0-beta.10", + "@perses-dev/client": "0.54.0-beta.11", "numbro": "^2.3.6", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-table": "^8.20.5", diff --git a/dashboards/package.json b/dashboards/package.json index a605be3b..30c92849 100644 --- a/dashboards/package.json +++ b/dashboards/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/dashboards", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "description": "The dashboards feature in Perses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -29,10 +29,10 @@ "lint:fix": "eslint --fix src --ext .ts,.tsx" }, "dependencies": { - "@perses-dev/components": "0.54.0-beta.10", - "@perses-dev/plugin-system": "0.54.0-beta.10", + "@perses-dev/components": "0.54.0-beta.11", + "@perses-dev/plugin-system": "0.54.0-beta.11", "@perses-dev/spec": "0.2.0-beta.6", - "@perses-dev/client": "0.54.0-beta.10", + "@perses-dev/client": "0.54.0-beta.11", "@tanstack/hotkeys": "^0.8.0", "@tanstack/react-hotkeys": "^0.9.1", "immer": "^10.1.1", diff --git a/explore/package.json b/explore/package.json index fe0ee174..f419a229 100644 --- a/explore/package.json +++ b/explore/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/explore", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "description": "The explore feature in Perses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -28,9 +28,9 @@ }, "dependencies": { "@nexucis/fuzzy": "^0.5.1", - "@perses-dev/components": "0.54.0-beta.10", - "@perses-dev/dashboards": "0.54.0-beta.10", - "@perses-dev/plugin-system": "0.54.0-beta.10", + "@perses-dev/components": "0.54.0-beta.11", + "@perses-dev/dashboards": "0.54.0-beta.11", + "@perses-dev/plugin-system": "0.54.0-beta.11", "mdi-material-ui": "^7.9.2", "qs": "^6.14.0", "react-virtuoso": "^4.12.2", diff --git a/package-lock.json b/package-lock.json index cf3a1865..aa0cc13c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "perses-shared", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "perses-shared", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "workspaces": [ "components", "dashboards", @@ -55,7 +55,7 @@ }, "client": { "name": "@perses-dev/client", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "license": "Apache-2.0", "dependencies": { "@perses-dev/spec": "0.2.0-beta.6", @@ -64,7 +64,7 @@ }, "components": { "name": "@perses-dev/components", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "license": "Apache-2.0", "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.4.0", @@ -73,7 +73,7 @@ "@date-fns/tz": "^1.4.1", "@fontsource/inter": "^5.0.0", "@mui/x-date-pickers": "^7.23.1", - "@perses-dev/client": "0.54.0-beta.10", + "@perses-dev/client": "0.54.0-beta.11", "@perses-dev/spec": "0.2.0-beta.6", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-table": "^8.20.5", @@ -105,12 +105,12 @@ }, "dashboards": { "name": "@perses-dev/dashboards", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "license": "Apache-2.0", "dependencies": { - "@perses-dev/client": "0.54.0-beta.10", - "@perses-dev/components": "0.54.0-beta.10", - "@perses-dev/plugin-system": "0.54.0-beta.10", + "@perses-dev/client": "0.54.0-beta.11", + "@perses-dev/components": "0.54.0-beta.11", + "@perses-dev/plugin-system": "0.54.0-beta.11", "@perses-dev/spec": "0.2.0-beta.6", "@tanstack/hotkeys": "^0.8.0", "@tanstack/react-hotkeys": "^0.9.1", @@ -177,13 +177,13 @@ }, "explore": { "name": "@perses-dev/explore", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "license": "Apache-2.0", "dependencies": { "@nexucis/fuzzy": "^0.5.1", - "@perses-dev/components": "0.54.0-beta.10", - "@perses-dev/dashboards": "0.54.0-beta.10", - "@perses-dev/plugin-system": "0.54.0-beta.10", + "@perses-dev/components": "0.54.0-beta.11", + "@perses-dev/dashboards": "0.54.0-beta.11", + "@perses-dev/plugin-system": "0.54.0-beta.11", "mdi-material-ui": "^7.9.2", "qs": "^6.14.0", "react-virtuoso": "^4.12.2", @@ -15699,12 +15699,12 @@ }, "plugin-system": { "name": "@perses-dev/plugin-system", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "license": "Apache-2.0", "dependencies": { "@module-federation/enhanced": "^2.6.0", - "@perses-dev/client": "0.54.0-beta.10", - "@perses-dev/components": "0.54.0-beta.10", + "@perses-dev/client": "0.54.0-beta.11", + "@perses-dev/components": "0.54.0-beta.11", "@perses-dev/core": "0.53.0", "@perses-dev/spec": "0.2.0-beta.6", "date-fns": "^4.1.0", diff --git a/package.json b/package.json index 5b4acc81..6b20520e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "perses-shared", "description": "Monorepo for the Perses UI shared packages", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "private": true, "scripts": { "build": "turbo run build", diff --git a/plugin-system/package.json b/plugin-system/package.json index 969ef381..189a185d 100644 --- a/plugin-system/package.json +++ b/plugin-system/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/plugin-system", - "version": "0.54.0-beta.10", + "version": "0.54.0-beta.11", "description": "The plugin feature in Pereses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -29,10 +29,10 @@ }, "dependencies": { "@module-federation/enhanced": "^2.6.0", - "@perses-dev/components": "0.54.0-beta.10", + "@perses-dev/components": "0.54.0-beta.11", "@perses-dev/core": "0.53.0", "@perses-dev/spec": "0.2.0-beta.6", - "@perses-dev/client": "0.54.0-beta.10", + "@perses-dev/client": "0.54.0-beta.11", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "immer": "^10.1.1", From 89f81f55bcfcfbb0a2b4b479fdc3b12f1b301f1d Mon Sep 17 00:00:00 2001 From: Jeremy Rickards <9338170+rickardsjp@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:44:47 +0200 Subject: [PATCH 03/17] [IGNORE] bump the plugin runtime dependencies (#193) The perses plugins declare a higher dependency, `v0.54.0-beta.8`. Because the host (Perses UI) and the plugin ranges don't intersect, but the shared deps are declared `singleton: true`, module federation loads a duplicate copy of `@perses-dev/plugin-system` into the plugin. That duplicate copy runs its own `createContext(undefined)` at module top level, giving the plugin its own `PluginRegistryContext`, a different React Context object than the one the host's `` provider binds to. Plugins that only export data (datasources, panels, queries) never exercise the duplicated context. LogExplorer is the first Explore plugin whose exposed component calls `usePluginRegistry` directly (I think?), so it hits the duplicate and throws "PluginRegistryContext not found. Did you forget a provider?" on `/explore`. Bump the shared entries to `0.54.0-beta.11`. `@perses-dev/core` stays at `0.53.1` as I understand it's being deprecated and no 0.54 release exists. Signed-off-by: Jeremy Rickards --- plugin-system/src/remote/PluginRuntime.tsx | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugin-system/src/remote/PluginRuntime.tsx b/plugin-system/src/remote/PluginRuntime.tsx index 67e24385..900da546 100644 --- a/plugin-system/src/remote/PluginRuntime.tsx +++ b/plugin-system/src/remote/PluginRuntime.tsx @@ -91,11 +91,11 @@ const getPluginRuntime = (): ModuleFederation => { }, }, '@perses-dev/spec': { - version: '0.2.0-beta.2', + version: '0.2.0-beta.6', lib: () => require('@perses-dev/spec'), shareConfig: { singleton: true, - requiredVersion: '^0.2.0-beta.2', + requiredVersion: '^0.2.0-beta.6', }, }, '@perses-dev/core': { @@ -107,43 +107,43 @@ const getPluginRuntime = (): ModuleFederation => { }, }, '@perses-dev/client': { - version: '0.54.0-beta.1', + version: '0.54.0-beta.11', lib: () => require('@perses-dev/client'), shareConfig: { singleton: true, - requiredVersion: '^0.54.0-beta.1', + requiredVersion: '^0.54.0-beta.11', }, }, '@perses-dev/components': { - version: '0.53.1', + version: '0.54.0-beta.11', lib: () => require('@perses-dev/components'), shareConfig: { singleton: true, - requiredVersion: '^0.53.1', + requiredVersion: '^0.54.0-beta.11', }, }, '@perses-dev/plugin-system': { - version: '0.53.1', + version: '0.54.0-beta.11', lib: () => require('@perses-dev/plugin-system'), shareConfig: { singleton: true, - requiredVersion: '^0.53.1', + requiredVersion: '^0.54.0-beta.11', }, }, '@perses-dev/explore': { - version: '0.53.1', + version: '0.54.0-beta.11', lib: () => require('@perses-dev/explore'), shareConfig: { singleton: true, - requiredVersion: '^0.53.1', + requiredVersion: '^0.54.0-beta.11', }, }, '@perses-dev/dashboards': { - version: '0.53.1', + version: '0.54.0-beta.11', lib: () => require('@perses-dev/dashboards'), shareConfig: { singleton: true, - requiredVersion: '^0.53.1', + requiredVersion: '^0.54.0-beta.11', }, }, // Below are the shared modules that are used by the plugins, this can be part of the SDK From 0145604c436b9bc8a1d4f3e087ea99c67ec95d21 Mon Sep 17 00:00:00 2001 From: Adrian <56292595+adrianSepiol@users.noreply.github.com> Date: Mon, 6 Jul 2026 13:08:07 +0200 Subject: [PATCH 04/17] [BREAKINGCHANGE] move PanelEditorValues and panel editor schemas out of @perses-dev/spec (#194) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BREAKINGCHANGE] move PanelEditorValues and panel editor schemas out of @perses-dev/spec Signed-off-by: Adrian Sepiół * [ENHANCEMENT] move PanelGroupId out of @perses-dev/spec into @perses-dev/plugin-system Signed-off-by: Adrian Sepiół --------- Signed-off-by: Adrian Sepiół --- components/package.json | 1 - components/src/LinksEditor/index.ts | 1 - .../src/components/GridLayout/GridLayout.tsx | 7 ++--- .../src/components/GridLayout/GridTitle.tsx | 3 +- dashboards/src/components/GridLayout/Row.tsx | 2 +- .../components/PanelDrawer/PanelDrawer.tsx | 5 ++-- .../PanelDrawer/PanelEditorForm.tsx | 6 ++-- .../components/PanelDrawer/PanelPreview.tsx | 2 +- .../PanelQueriesSharedControls.tsx | 10 +++++-- .../dashboard-provider-api.ts | 3 +- .../delete-panel-group-slice.ts | 2 +- .../DashboardProvider/panel-editor-slice.ts | 2 +- .../panel-group-editor-slice.ts | 2 +- .../DashboardProvider/panel-group-slice.ts | 3 +- .../DashboardProvider/view-panel-slice.ts | 2 +- dashboards/src/context/useDashboard.tsx | 3 +- dashboards/src/model/PanelGroupDefinition.ts | 3 +- package-lock.json | 1 - .../components}/LinksEditor/LinksEditor.tsx | 4 +-- .../src/components/LinksEditor/index.ts | 14 ++++++++++ .../PanelSpecEditor/PanelSpecEditor.test.tsx | 2 +- .../PanelSpecEditor/PanelSpecEditor.tsx | 9 +++--- plugin-system/src/components/index.ts | 1 + .../src/context/ValidationProvider.tsx | 13 ++++----- plugin-system/src/index.ts | 1 + plugin-system/src/model/panels.ts | 12 +++++++- plugin-system/src/schema/index.ts | 14 ++++++++++ plugin-system/src/schema/panel.ts | 28 +++++++++++++++++++ 28 files changed, 113 insertions(+), 43 deletions(-) rename {components/src => plugin-system/src/components}/LinksEditor/LinksEditor.tsx (97%) create mode 100644 plugin-system/src/components/LinksEditor/index.ts create mode 100644 plugin-system/src/schema/index.ts create mode 100644 plugin-system/src/schema/panel.ts diff --git a/components/package.json b/components/package.json index ca69cb5c..53a08e39 100644 --- a/components/package.json +++ b/components/package.json @@ -49,7 +49,6 @@ "notistack": "^3.0.2", "react-colorful": "^5.6.1", "react-error-boundary": "^3.1.4", - "react-hook-form": "^7.51.3", "react-virtuoso": "^4.12.2" }, "devDependencies": { diff --git a/components/src/LinksEditor/index.ts b/components/src/LinksEditor/index.ts index 3547b52a..59bfccf0 100644 --- a/components/src/LinksEditor/index.ts +++ b/components/src/LinksEditor/index.ts @@ -11,5 +11,4 @@ // See the License for the specific language governing permissions and // limitations under the License. -export * from './LinksEditor'; export * from './LinkEditorForm'; diff --git a/dashboards/src/components/GridLayout/GridLayout.tsx b/dashboards/src/components/GridLayout/GridLayout.tsx index 85126fb5..39784812 100644 --- a/dashboards/src/components/GridLayout/GridLayout.tsx +++ b/dashboards/src/components/GridLayout/GridLayout.tsx @@ -11,14 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. import { ReactElement, useState } from 'react'; -import { Layouts, Layout } from 'react-grid-layout'; +import { Layout, Layouts } from 'react-grid-layout'; -import { PanelGroupId } from '@perses-dev/spec'; -import { useVariableValues, VariableContext } from '@perses-dev/plugin-system'; +import { useVariableValues, VariableContext, PanelGroupId } from '@perses-dev/plugin-system'; +import { PanelGroupDefinition } from '../../model'; import { useEditMode, usePanelGroup, usePanelGroupActions, useViewPanelGroup } from '../../context'; import { GRID_LAYOUT_SMALL_BREAKPOINT } from '../../constants'; import { PanelOptions } from '../Panel'; -import { PanelGroupDefinition } from '../../model'; import { Row, RowProps } from './Row'; export interface GridLayoutProps { diff --git a/dashboards/src/components/GridLayout/GridTitle.tsx b/dashboards/src/components/GridLayout/GridTitle.tsx index f88e8ae8..850777aa 100644 --- a/dashboards/src/components/GridLayout/GridTitle.tsx +++ b/dashboards/src/components/GridLayout/GridTitle.tsx @@ -20,8 +20,7 @@ import ArrowUpIcon from 'mdi-material-ui/ArrowUp'; import ArrowDownIcon from 'mdi-material-ui/ArrowDown'; import DeleteIcon from 'mdi-material-ui/DeleteOutline'; import { InfoTooltip } from '@perses-dev/components'; -import { useReplaceVariablesInString } from '@perses-dev/plugin-system'; -import { PanelGroupId } from '@perses-dev/spec'; +import { useReplaceVariablesInString, PanelGroupId } from '@perses-dev/plugin-system'; import { ReactElement } from 'react'; import { ARIA_LABEL_TEXT, TOOLTIP_TEXT } from '../../constants'; import { usePanelGroupActions, useEditMode, useDeletePanelGroupDialog } from '../../context'; diff --git a/dashboards/src/components/GridLayout/Row.tsx b/dashboards/src/components/GridLayout/Row.tsx index 7b5fa36a..2c062b0f 100644 --- a/dashboards/src/components/GridLayout/Row.tsx +++ b/dashboards/src/components/GridLayout/Row.tsx @@ -12,11 +12,11 @@ // limitations under the License. import { Collapse, useTheme } from '@mui/material'; -import { PanelGroupId } from '@perses-dev/spec'; import { PanelOptions, useViewPanelGroup } from '@perses-dev/dashboards'; import { ReactElement, useEffect, useMemo, useState } from 'react'; import { Layout, Layouts, Responsive, WidthProvider } from 'react-grid-layout'; import { ErrorAlert, ErrorBoundary } from '@perses-dev/components'; +import { PanelGroupId } from '@perses-dev/plugin-system'; import { GRID_LAYOUT_COLS, GRID_LAYOUT_SMALL_BREAKPOINT } from '../../constants'; import { PanelGroupDefinition, PanelGroupItemLayout } from '../../model'; import { GridContainer } from './GridContainer'; diff --git a/dashboards/src/components/PanelDrawer/PanelDrawer.tsx b/dashboards/src/components/PanelDrawer/PanelDrawer.tsx index eec9b1ab..4ed1b421 100644 --- a/dashboards/src/components/PanelDrawer/PanelDrawer.tsx +++ b/dashboards/src/components/PanelDrawer/PanelDrawer.tsx @@ -25,10 +25,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { ReactElement, useState, useMemo, ReactNode, useCallback } from 'react'; +import { ReactElement, ReactNode, useCallback, useMemo, useState } from 'react'; import { Drawer, ErrorAlert, ErrorBoundary } from '@perses-dev/components'; -import { PanelEditorValues } from '@perses-dev/spec'; -import { useVariableValues, VariableContext } from '@perses-dev/plugin-system'; +import { PanelEditorValues, useVariableValues, VariableContext } from '@perses-dev/plugin-system'; import { usePanelEditor, usePanelKey } from '../../context'; import { PanelEditorForm } from './PanelEditorForm'; diff --git a/dashboards/src/components/PanelDrawer/PanelEditorForm.tsx b/dashboards/src/components/PanelDrawer/PanelEditorForm.tsx index 4d72e5f0..a1cfdba8 100644 --- a/dashboards/src/components/PanelDrawer/PanelEditorForm.tsx +++ b/dashboards/src/components/PanelDrawer/PanelEditorForm.tsx @@ -13,15 +13,15 @@ import { ReactElement, useCallback, useEffect, useState } from 'react'; import { Box, Button, Grid, MenuItem, Stack, TextField, Typography } from '@mui/material'; -import { PanelDefinition, PanelEditorValues } from '@perses-dev/spec'; +import { PanelDefinition } from '@perses-dev/spec'; +import { PanelEditorValues, PluginKindSelect, usePluginEditor, useValidationSchemas } from '@perses-dev/plugin-system'; import { DiscardChangesConfirmationDialog, ErrorAlert, ErrorBoundary, - getTitleAction, getSubmitText, + getTitleAction, } from '@perses-dev/components'; -import { PluginKindSelect, usePluginEditor, useValidationSchemas } from '@perses-dev/plugin-system'; import { Controller, FormProvider, SubmitHandler, useForm, useWatch } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { Action } from '@perses-dev/client'; diff --git a/dashboards/src/components/PanelDrawer/PanelPreview.tsx b/dashboards/src/components/PanelDrawer/PanelPreview.tsx index 606b7b66..13aeb073 100644 --- a/dashboards/src/components/PanelDrawer/PanelPreview.tsx +++ b/dashboards/src/components/PanelDrawer/PanelPreview.tsx @@ -13,7 +13,7 @@ import { ReactElement, useContext, useEffect, useRef } from 'react'; import { Box } from '@mui/material'; -import { PanelEditorValues } from '@perses-dev/spec'; +import { PanelEditorValues } from '@perses-dev/plugin-system'; import { Panel } from '../Panel'; import { PanelEditorContext } from '../../context'; diff --git a/dashboards/src/components/PanelDrawer/PanelQueriesSharedControls.tsx b/dashboards/src/components/PanelDrawer/PanelQueriesSharedControls.tsx index 65e2f506..f1e3a870 100644 --- a/dashboards/src/components/PanelDrawer/PanelQueriesSharedControls.tsx +++ b/dashboards/src/components/PanelDrawer/PanelQueriesSharedControls.tsx @@ -14,8 +14,14 @@ import { Grid, Typography } from '@mui/material'; import { ErrorAlert, ErrorBoundary } from '@perses-dev/components'; import { PanelEditorContext, PanelPreview } from '@perses-dev/dashboards'; -import { DataQueriesProvider, PanelSpecEditor, usePlugin, useSuggestedStepMs } from '@perses-dev/plugin-system'; -import { Definition, PanelDefinition, PanelEditorValues, QueryDefinition, UnknownSpec } from '@perses-dev/spec'; +import { + DataQueriesProvider, + PanelEditorValues, + PanelSpecEditor, + usePlugin, + useSuggestedStepMs, +} from '@perses-dev/plugin-system'; +import { Definition, PanelDefinition, QueryDefinition, UnknownSpec } from '@perses-dev/spec'; import { Control } from 'react-hook-form'; import { ReactElement, useCallback, useContext, useMemo, useState } from 'react'; diff --git a/dashboards/src/context/DashboardProvider/dashboard-provider-api.ts b/dashboards/src/context/DashboardProvider/dashboard-provider-api.ts index a650ac9c..046df30f 100644 --- a/dashboards/src/context/DashboardProvider/dashboard-provider-api.ts +++ b/dashboards/src/context/DashboardProvider/dashboard-provider-api.ts @@ -12,8 +12,9 @@ // limitations under the License. import { useCallback, useMemo } from 'react'; -import { DurationString, Link, PanelDefinition, PanelGroupId } from '@perses-dev/spec'; +import { DurationString, Link, PanelDefinition } from '@perses-dev/spec'; import { DashboardResource } from '@perses-dev/client'; +import { PanelGroupId } from '@perses-dev/plugin-system'; import { PanelGroupDefinition, PanelGroupItemId, PanelGroupItemLayout } from '../../model'; import { DashboardStoreState, useDashboardStore } from './DashboardProvider'; import { DeletePanelGroupDialogState } from './delete-panel-group-slice'; diff --git a/dashboards/src/context/DashboardProvider/delete-panel-group-slice.ts b/dashboards/src/context/DashboardProvider/delete-panel-group-slice.ts index 33eabdff..3f1685d0 100644 --- a/dashboards/src/context/DashboardProvider/delete-panel-group-slice.ts +++ b/dashboards/src/context/DashboardProvider/delete-panel-group-slice.ts @@ -12,7 +12,7 @@ // limitations under the License. import { StateCreator } from 'zustand'; -import { PanelGroupId } from '@perses-dev/spec'; +import { PanelGroupId } from '@perses-dev/plugin-system'; import { Middleware } from './common'; import { PanelGroupSlice } from './panel-group-slice'; import { PanelSlice } from './panel-slice'; diff --git a/dashboards/src/context/DashboardProvider/panel-editor-slice.ts b/dashboards/src/context/DashboardProvider/panel-editor-slice.ts index 5040ebcb..7d413314 100644 --- a/dashboards/src/context/DashboardProvider/panel-editor-slice.ts +++ b/dashboards/src/context/DashboardProvider/panel-editor-slice.ts @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { PanelEditorValues, PanelGroupId } from '@perses-dev/spec'; +import { PanelEditorValues, PanelGroupId } from '@perses-dev/plugin-system'; import { StateCreator } from 'zustand'; import { Action } from '@perses-dev/client'; import { generatePanelKey, getYForNewRow } from '../../utils'; diff --git a/dashboards/src/context/DashboardProvider/panel-group-editor-slice.ts b/dashboards/src/context/DashboardProvider/panel-group-editor-slice.ts index 250c808c..4b777695 100644 --- a/dashboards/src/context/DashboardProvider/panel-group-editor-slice.ts +++ b/dashboards/src/context/DashboardProvider/panel-group-editor-slice.ts @@ -12,7 +12,7 @@ // limitations under the License. import { StateCreator } from 'zustand'; -import { PanelGroupId } from '@perses-dev/spec'; +import { PanelGroupId } from '@perses-dev/plugin-system'; import { Middleware } from './common'; import { PanelGroupSlice, addPanelGroup, createEmptyPanelGroup } from './panel-group-slice'; diff --git a/dashboards/src/context/DashboardProvider/panel-group-slice.ts b/dashboards/src/context/DashboardProvider/panel-group-slice.ts index 421e6bf8..dcb971ac 100644 --- a/dashboards/src/context/DashboardProvider/panel-group-slice.ts +++ b/dashboards/src/context/DashboardProvider/panel-group-slice.ts @@ -11,9 +11,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { getPanelKeyFromRef, LayoutDefinition, PanelGroupId } from '@perses-dev/spec'; +import { getPanelKeyFromRef, LayoutDefinition } from '@perses-dev/spec'; import { StateCreator } from 'zustand'; import { WritableDraft } from 'immer'; +import { PanelGroupId } from '@perses-dev/plugin-system'; import { PanelGroupDefinition } from '../../model'; import { generateId, Middleware } from './common'; diff --git a/dashboards/src/context/DashboardProvider/view-panel-slice.ts b/dashboards/src/context/DashboardProvider/view-panel-slice.ts index aa030bde..62747108 100644 --- a/dashboards/src/context/DashboardProvider/view-panel-slice.ts +++ b/dashboards/src/context/DashboardProvider/view-panel-slice.ts @@ -12,7 +12,7 @@ // limitations under the License. import { StateCreator } from 'zustand'; -import { PanelGroupId } from '@perses-dev/spec'; +import { PanelGroupId } from '@perses-dev/plugin-system'; import { PanelGroupDefinition, PanelGroupItemId } from '../../model'; import { Middleware } from './common'; import { PanelGroupSlice } from './panel-group-slice'; diff --git a/dashboards/src/context/useDashboard.tsx b/dashboards/src/context/useDashboard.tsx index ccebf7b1..e140a45c 100644 --- a/dashboards/src/context/useDashboard.tsx +++ b/dashboards/src/context/useDashboard.tsx @@ -11,8 +11,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { createPanelRef, DashboardSpec, DurationString, GridDefinition, PanelGroupId } from '@perses-dev/spec'; +import { createPanelRef, DashboardSpec, DurationString, GridDefinition } from '@perses-dev/spec'; import { DashboardResource } from '@perses-dev/client'; +import { PanelGroupId } from '@perses-dev/plugin-system'; import { PanelGroupDefinition } from '../model'; import { useDashboardStore } from './DashboardProvider'; diff --git a/dashboards/src/model/PanelGroupDefinition.ts b/dashboards/src/model/PanelGroupDefinition.ts index 82adffe7..171da4ae 100644 --- a/dashboards/src/model/PanelGroupDefinition.ts +++ b/dashboards/src/model/PanelGroupDefinition.ts @@ -11,8 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -export type PanelGroupId = number; - +import { PanelGroupId } from '@perses-dev/plugin-system'; /** * Panel Group Item Layout ID type. String identifier for items within a panel group. */ diff --git a/package-lock.json b/package-lock.json index aa0cc13c..925b64af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,7 +88,6 @@ "numbro": "^2.3.6", "react-colorful": "^5.6.1", "react-error-boundary": "^3.1.4", - "react-hook-form": "^7.51.3", "react-virtuoso": "^4.12.2" }, "devDependencies": { diff --git a/components/src/LinksEditor/LinksEditor.tsx b/plugin-system/src/components/LinksEditor/LinksEditor.tsx similarity index 97% rename from components/src/LinksEditor/LinksEditor.tsx rename to plugin-system/src/components/LinksEditor/LinksEditor.tsx index 9ce02ec9..93c2a0bc 100644 --- a/components/src/LinksEditor/LinksEditor.tsx +++ b/plugin-system/src/components/LinksEditor/LinksEditor.tsx @@ -16,8 +16,8 @@ import { Divider, IconButton, Stack, Typography } from '@mui/material'; import { Controller, useFieldArray, Control } from 'react-hook-form'; import PlusIcon from 'mdi-material-ui/Plus'; import MinusIcon from 'mdi-material-ui/Minus'; -import { PanelEditorValues } from '@perses-dev/spec'; -import { LinkEditorForm } from './LinkEditorForm'; +import { LinkEditorForm } from '@perses-dev/components'; +import { PanelEditorValues } from '../../model'; export interface LinksEditorProps extends HTMLAttributes { control: Control; diff --git a/plugin-system/src/components/LinksEditor/index.ts b/plugin-system/src/components/LinksEditor/index.ts new file mode 100644 index 00000000..2dc504b3 --- /dev/null +++ b/plugin-system/src/components/LinksEditor/index.ts @@ -0,0 +1,14 @@ +// Copyright 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 './LinksEditor'; diff --git a/plugin-system/src/components/PanelSpecEditor/PanelSpecEditor.test.tsx b/plugin-system/src/components/PanelSpecEditor/PanelSpecEditor.test.tsx index 3ff23401..37963bec 100644 --- a/plugin-system/src/components/PanelSpecEditor/PanelSpecEditor.test.tsx +++ b/plugin-system/src/components/PanelSpecEditor/PanelSpecEditor.test.tsx @@ -13,8 +13,8 @@ import { screen } from '@testing-library/react'; import { useForm } from 'react-hook-form'; -import { PanelEditorValues } from '@perses-dev/spec'; import { ReactElement } from 'react'; +import { PanelEditorValues } from '../../model'; import { renderWithContext } from '../../test'; import { DataQueriesContext } from '../../runtime'; import { PanelSpecEditor, PanelSpecEditorProps } from './PanelSpecEditor'; diff --git a/plugin-system/src/components/PanelSpecEditor/PanelSpecEditor.tsx b/plugin-system/src/components/PanelSpecEditor/PanelSpecEditor.tsx index b77a1f7d..eeb92e75 100644 --- a/plugin-system/src/components/PanelSpecEditor/PanelSpecEditor.tsx +++ b/plugin-system/src/components/PanelSpecEditor/PanelSpecEditor.tsx @@ -11,13 +11,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { ErrorAlert, JSONEditor, LinksEditor } from '@perses-dev/components'; -import { PanelDefinition, PanelEditorValues, QueryDefinition, UnknownSpec } from '@perses-dev/spec'; +import { ErrorAlert, JSONEditor } from '@perses-dev/components'; +import { PanelDefinition, QueryDefinition, UnknownSpec } from '@perses-dev/spec'; import { Control, Controller } from 'react-hook-form'; import { forwardRef, ReactElement } from 'react'; +import { LinksEditor } from '../LinksEditor'; +import { PanelEditorValues, PanelPlugin } from '../../model'; import { useDataQueriesContext, usePlugin } from '../../runtime'; -import { PanelPlugin } from '../../model'; -import { OptionsEditorTabsProps, OptionsEditorTabs } from '../OptionsEditorTabs'; +import { OptionsEditorTabs, OptionsEditorTabsProps } from '../OptionsEditorTabs'; import { MultiQueryEditor } from '../MultiQueryEditor'; import { PluginEditorRef } from '../PluginEditor'; diff --git a/plugin-system/src/components/index.ts b/plugin-system/src/components/index.ts index 460fdf25..da728a4c 100644 --- a/plugin-system/src/components/index.ts +++ b/plugin-system/src/components/index.ts @@ -18,6 +18,7 @@ export * from './DatasourceSelect'; export * from './HTTPSettingsEditor'; export * from './ItemSelectionActionsOptionsEditor'; export * from './LegendOptionsEditor'; +export * from './LinksEditor'; export * from './MultiQueryEditor'; export * from './OptionsEditorRadios'; export * from './OptionsEditorTabs'; diff --git a/plugin-system/src/context/ValidationProvider.tsx b/plugin-system/src/context/ValidationProvider.tsx index 13000f84..5f9a77c5 100644 --- a/plugin-system/src/context/ValidationProvider.tsx +++ b/plugin-system/src/context/ValidationProvider.tsx @@ -13,20 +13,19 @@ import { createContext, ReactElement, ReactNode, useContext, useState } from 'react'; import { - PanelEditorValues, - VariableDefinition, - PluginSchema, - panelEditorSchema as defaultPanelEditorSchema, - variableDefinitionSchema, - buildPanelEditorSchema, - buildVariableDefinitionSchema, AnnotationSpec, annotationSpecSchema, buildAnnotationSpecSchema, + buildVariableDefinitionSchema, + PluginSchema, + VariableDefinition, + variableDefinitionSchema, } from '@perses-dev/spec'; import { z } from 'zod'; import { buildDatasourceDefinitionSchema, DatasourceDefinition, datasourceDefinitionSchema } from '@perses-dev/client'; +import { buildPanelEditorSchema, panelEditorSchema as defaultPanelEditorSchema } from '../schema'; +import { PanelEditorValues } from '../model'; export interface ValidationSchemas { datasourceEditorSchema: z.Schema; diff --git a/plugin-system/src/index.ts b/plugin-system/src/index.ts index 806a3170..e38fa3d6 100644 --- a/plugin-system/src/index.ts +++ b/plugin-system/src/index.ts @@ -15,6 +15,7 @@ export * from './components'; export * from './constants'; export * from './model'; export * from './runtime'; +export * from './schema'; export * from './test-utils'; export * from './utils'; export * from './context'; diff --git a/plugin-system/src/model/panels.ts b/plugin-system/src/model/panels.ts index b28eb8ec..d75e1452 100644 --- a/plugin-system/src/model/panels.ts +++ b/plugin-system/src/model/panels.ts @@ -12,7 +12,7 @@ // limitations under the License. import React from 'react'; -import { UnknownSpec, PanelDefinition, QueryPluginType, QueryDataType, QueryDefinition } from '@perses-dev/spec'; +import { PanelDefinition, QueryDataType, QueryDefinition, QueryPluginType, UnknownSpec } from '@perses-dev/spec'; import { OptionsEditorTab } from '../components'; import { QueryOptions } from '../runtime'; import { OptionsEditorProps, Plugin } from './plugin-base'; @@ -79,3 +79,13 @@ export interface PanelData { definition: QueryDefinition; data: SupportedQueryTypes; } + +export type PanelGroupId = number; + +/** + * Panel values that can be edited in the panel editor. + */ +export interface PanelEditorValues { + groupId: PanelGroupId; + panelDefinition: PanelDefinition; +} diff --git a/plugin-system/src/schema/index.ts b/plugin-system/src/schema/index.ts new file mode 100644 index 00000000..bd1aeda0 --- /dev/null +++ b/plugin-system/src/schema/index.ts @@ -0,0 +1,14 @@ +// Copyright 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 './panel'; diff --git a/plugin-system/src/schema/panel.ts b/plugin-system/src/schema/panel.ts new file mode 100644 index 00000000..6f68e10b --- /dev/null +++ b/plugin-system/src/schema/panel.ts @@ -0,0 +1,28 @@ +// Copyright 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 { z } from 'zod'; +import { PluginSchema, panelDefinitionSchema, buildPanelDefinitionSchema } from '@perses-dev/spec'; +import { PanelEditorValues } from '../model'; + +export const panelEditorSchema: z.ZodSchema = z.object({ + groupId: z.number(), + panelDefinition: panelDefinitionSchema, +}); + +export function buildPanelEditorSchema(pluginSchema: PluginSchema): z.ZodSchema { + return z.object({ + groupId: z.number(), + panelDefinition: buildPanelDefinitionSchema(pluginSchema), + }); +} From 9668451739f7714b79e3870236403c532a3ae5dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:14:30 +0200 Subject: [PATCH 05/17] Bump golangci/golangci-lint-action in the actions group (#196) Bumps the actions group with 1 update: [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action). Updates `golangci/golangci-lint-action` from 9.2.1 to 9.3.0 - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v9.2.1...v9.3.0) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-version: 9.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 08cc119d..84a961e0 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -24,7 +24,7 @@ jobs: enable_go_cache: false enable_npm: false - name: golangci-lint - uses: golangci/golangci-lint-action@v9.2.1 + uses: golangci/golangci-lint-action@v9.3.0 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. version: v2.12.2 From 452c208d0aac87ce6c246f3219c27ef774ed31a9 Mon Sep 17 00:00:00 2001 From: Akshay Iyyadurai Balasundaram Date: Tue, 7 Jul 2026 17:24:34 +0530 Subject: [PATCH 06/17] [BUGFIX] fix timeseries tooltip hover for stacked and grouped charts (#195) * [BUGFIX] fix timeseries tooltip hover for stacked and grouped charts Port of perses/perses#3574 by @Gavriel-M. Signed-off-by: Akshay Iyyadurai Balasundaram * fix lint Signed-off-by: Akshay Iyyadurai Balasundaram * fix review comment Signed-off-by: Akshay Iyyadurai Balasundaram * fix lint Signed-off-by: Akshay Iyyadurai Balasundaram --------- Signed-off-by: Akshay Iyyadurai Balasundaram --- .../TimeSeriesTooltip/TimeChartTooltip.tsx | 35 +- .../src/TimeSeriesTooltip/TooltipContent.tsx | 2 +- .../src/TimeSeriesTooltip/TooltipHeader.tsx | 2 +- components/src/TimeSeriesTooltip/index.ts | 1 + .../TimeSeriesTooltip/nearby-series.test.ts | 160 +++++- .../src/TimeSeriesTooltip/nearby-series.ts | 487 +++++++++++------- .../src/TimeSeriesTooltip/tooltip-model.ts | 2 +- components/src/TimeSeriesTooltip/types.ts | 46 ++ .../src/TimeSeriesTooltip/utils.test.ts | 158 ++++++ components/src/TimeSeriesTooltip/utils.ts | 139 ++++- components/src/utils/chart-actions.test.ts | 75 ++- components/src/utils/chart-actions.ts | 15 +- 12 files changed, 907 insertions(+), 215 deletions(-) create mode 100644 components/src/TimeSeriesTooltip/types.ts create mode 100644 components/src/TimeSeriesTooltip/utils.test.ts diff --git a/components/src/TimeSeriesTooltip/TimeChartTooltip.tsx b/components/src/TimeSeriesTooltip/TimeChartTooltip.tsx index db7fe68f..42d1cf1d 100644 --- a/components/src/TimeSeriesTooltip/TimeChartTooltip.tsx +++ b/components/src/TimeSeriesTooltip/TimeChartTooltip.tsx @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { memo, MutableRefObject, useRef, useState } from 'react'; +import { memo, MutableRefObject, useCallback, useLayoutEffect, useRef, useState } from 'react'; import { Box, Portal, Stack } from '@mui/material'; import { ECharts as EChartsInstance } from 'echarts/core'; import { TimeSeries } from '@perses-dev/spec'; @@ -57,26 +57,49 @@ export const TimeChartTooltip = memo(function TimeChartTooltip({ }: TimeChartTooltipProps) { const [showAllSeries, setShowAllSeries] = useState(false); const transform = useRef(); + const tooltipElementRef = useRef(null); const mousePos = useMousePosition(); const { height, width, ref: tooltipRef } = useResizeObserver(); const isTooltipPinned = pinnedPos !== null && enablePinning; + // Stable callback — prevents the ResizeObserver from disconnecting/reconnecting on every render. + const setTooltipRef = useCallback( + (node: HTMLDivElement | null): void => { + tooltipElementRef.current = node; + tooltipRef(node); + }, + [tooltipRef] + ); + + const containerElement = containerId ? document.querySelector(containerId) : undefined; + + // Synchronously reposition after every render to prevent one-frame viewport overflow. + useLayoutEffect(() => { + if (mousePos === null) return; + const node = tooltipElementRef.current; + if (!node) return; + const rect = node.getBoundingClientRect(); + if (rect.height === 0 || rect.width === 0) return; + const nextTransform = assembleTransform(mousePos, pinnedPos, rect.height, rect.width, containerElement); + if (nextTransform && nextTransform !== transform.current) { + transform.current = nextTransform; + node.style.transform = nextTransform; + } + }); + if (mousePos === null || mousePos.target === null || data === null) return null; - // Ensure user is hovering over a chart before checking for nearby series. if (pinnedPos === null && (mousePos.target as HTMLElement).tagName !== 'CANVAS') return null; const chart = chartRef.current; - const containerElement = containerId ? document.querySelector(containerId) : undefined; - // if tooltip is attached to a container, set max height to the height of the container so tooltip does not get cut off + // Cap height to container so the tooltip is not cut off. const maxHeight = containerElement ? containerElement.getBoundingClientRect().height : undefined; transform.current = assembleTransform(mousePos, pinnedPos, height ?? 0, width ?? 0, containerElement); - // Get series nearby the cursor and pass into tooltip content children. const nearbySeries = getNearbySeriesData({ mousePos, data, @@ -96,7 +119,7 @@ export const TimeChartTooltip = memo(function TimeChartTooltip({ return ( getTooltipStyles(theme, pinnedPos, maxHeight)} style={{ transform: transform.current, diff --git a/components/src/TimeSeriesTooltip/TooltipContent.tsx b/components/src/TimeSeriesTooltip/TooltipContent.tsx index 59227aaf..862e068b 100644 --- a/components/src/TimeSeriesTooltip/TooltipContent.tsx +++ b/components/src/TimeSeriesTooltip/TooltipContent.tsx @@ -13,7 +13,7 @@ import { ReactElement, useMemo } from 'react'; import { Box } from '@mui/material'; -import { NearbySeriesArray } from './nearby-series'; +import { NearbySeriesArray } from './types'; import { SeriesInfo } from './SeriesInfo'; export interface TooltipContentProps { diff --git a/components/src/TimeSeriesTooltip/TooltipHeader.tsx b/components/src/TimeSeriesTooltip/TooltipHeader.tsx index 56dc677b..d556521d 100644 --- a/components/src/TimeSeriesTooltip/TooltipHeader.tsx +++ b/components/src/TimeSeriesTooltip/TooltipHeader.tsx @@ -16,7 +16,7 @@ import Pin from 'mdi-material-ui/Pin'; import PinOutline from 'mdi-material-ui/PinOutline'; import { memo, ReactElement } from 'react'; import { useTimeZone } from '../context/TimeZoneProvider'; -import { NearbySeriesArray } from './nearby-series'; +import { NearbySeriesArray } from './types'; import { TOOLTIP_BG_COLOR_FALLBACK, TOOLTIP_MAX_WIDTH, diff --git a/components/src/TimeSeriesTooltip/index.ts b/components/src/TimeSeriesTooltip/index.ts index fd2a2d08..02c39e87 100644 --- a/components/src/TimeSeriesTooltip/index.ts +++ b/components/src/TimeSeriesTooltip/index.ts @@ -19,4 +19,5 @@ export * from './TooltipContent'; export * from './TooltipHeader'; export * from './nearby-series'; export * from './tooltip-model'; +export * from './types'; export * from './utils'; diff --git a/components/src/TimeSeriesTooltip/nearby-series.test.ts b/components/src/TimeSeriesTooltip/nearby-series.test.ts index d0bfae26..35b6d73d 100644 --- a/components/src/TimeSeriesTooltip/nearby-series.test.ts +++ b/components/src/TimeSeriesTooltip/nearby-series.test.ts @@ -11,8 +11,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { EChartsDataFormat, FormatOptions } from '../model'; -import { legacyCheckforNearbySeries, getYBuffer, isWithinPercentageRange } from './nearby-series'; +import { ECharts as EChartsInstance } from 'echarts/core'; +import { TimeSeries } from '@perses-dev/spec'; +import { EChartsDataFormat, FormatOptions, TimeChartSeriesMapping } from '../model'; +import { + checkforNearbyTimeSeries, + legacyCheckforNearbySeries, + getYBuffer, + isWithinPercentageRange, +} from './nearby-series'; +import { calculateVisualYForSeries } from './utils'; describe('legacyCheckforNearbySeries', () => { const chartData: EChartsDataFormat = { @@ -122,3 +130,151 @@ describe('isWithinPercentageRange', () => { expect(result).toBe(false); }); }); + +describe('calculateVisualYForSeries', () => { + it('returns the raw yValue for non-stacked series and does not touch the totals map', () => { + const seriesMapping = [{ type: 'line', name: 'a' }] as unknown as TimeChartSeriesMapping; + const totals = new Map(); + + const result = calculateVisualYForSeries(0, 42, seriesMapping, totals); + + expect(result).toBe(42); + expect(totals.size).toBe(0); + }); + + it('accumulates stacked totals per stack id across sequential calls', () => { + const seriesMapping = [ + { type: 'line', name: 'a', stack: 'total' }, + { type: 'line', name: 'b', stack: 'total' }, + { type: 'line', name: 'c', stack: 'total' }, + ] as unknown as TimeChartSeriesMapping; + + const totals = new Map(); + + expect(calculateVisualYForSeries(0, 10, seriesMapping, totals)).toBe(10); + expect(calculateVisualYForSeries(1, 20, seriesMapping, totals)).toBe(30); + expect(calculateVisualYForSeries(2, 5, seriesMapping, totals)).toBe(35); + expect(totals.get('total')).toBe(35); + }); + + it('keeps totals separate across different stack ids', () => { + const seriesMapping = [ + { type: 'line', name: 'a', stack: 'left' }, + { type: 'line', name: 'b', stack: 'right' }, + { type: 'line', name: 'c', stack: 'left' }, + ] as unknown as TimeChartSeriesMapping; + + const totals = new Map(); + + expect(calculateVisualYForSeries(0, 100, seriesMapping, totals)).toBe(100); + expect(calculateVisualYForSeries(1, 50, seriesMapping, totals)).toBe(50); + expect(calculateVisualYForSeries(2, 25, seriesMapping, totals)).toBe(125); + expect(totals.get('left')).toBe(125); + expect(totals.get('right')).toBe(50); + }); +}); + +describe('checkforNearbyTimeSeries — stacked lines', () => { + const TIMESTAMP = 1_700_000_000_000; + + function buildStackedFixture(): { + data: TimeSeries[]; + seriesMapping: TimeChartSeriesMapping; + } { + const data: TimeSeries[] = [ + { name: 'series-a', values: [[TIMESTAMP, 10]] }, + { name: 'series-b', values: [[TIMESTAMP, 20]] }, + ]; + const seriesMapping = [ + { type: 'line', name: 'series-a', color: '#111', stack: 'group', id: 'a' }, + { type: 'line', name: 'series-b', color: '#222', stack: 'group', id: 'b' }, + ] as unknown as TimeChartSeriesMapping; + return { data, seriesMapping }; + } + + function buildChartMock(): { + chart: EChartsInstance; + dispatched: Array<{ type: string; seriesIndex?: number | number[]; dataIndex?: number }>; + } { + const dispatched: Array<{ type: string; seriesIndex?: number | number[]; dataIndex?: number }> = []; + const chart = { + dispatchAction: (action: { type: string; seriesIndex?: number | number[]; dataIndex?: number }): void => { + dispatched.push(action); + }, + // Simple identity-style mock: return the value passed in as pixel Y. + convertToPixel: (_finder: unknown, value: number[]): number[] => [value[0] ?? 0, value[1] ?? 0], + getDom: (): null => null, + } as unknown as EChartsInstance; + return { chart, dispatched }; + } + + it('emphasizes the visually-hovered stacked series (top of stack), not the one with the matching raw value', () => { + const { data, seriesMapping } = buildStackedFixture(); + const { chart, dispatched } = buildChartMock(); + + const yBuffer = 5; + const result = checkforNearbyTimeSeries(data, seriesMapping, [TIMESTAMP, 30], yBuffer, chart); + + const winner = result.find((series) => series.isClosestToCursor); + expect(winner).toBeDefined(); + expect(winner?.seriesName).toBe('series-b'); + // y should be the raw per-series value (20), not the accumulated visual Y (30) + expect(winner?.y).toBe(20); + + const seriesA = result.find((series) => series.seriesName === 'series-a'); + expect(seriesA).toBeUndefined(); + + // Non-candidate series (a) must be explicitly downplayed to clear stale emphasis. + const downplays = dispatched.filter((action) => action.type === 'downplay'); + const downplayedSeriesIdxs = new Set(); + for (const action of downplays) { + if (Array.isArray(action.seriesIndex)) { + for (const idx of action.seriesIndex) downplayedSeriesIdxs.add(idx); + } else if (typeof action.seriesIndex === 'number') { + downplayedSeriesIdxs.add(action.seriesIndex); + } + } + expect(downplayedSeriesIdxs.has(0)).toBe(true); + }); + + it('emphasizes series-a when the cursor is near its visual position (bottom of stack)', () => { + const { data, seriesMapping } = buildStackedFixture(); + const { chart } = buildChartMock(); + + const result = checkforNearbyTimeSeries(data, seriesMapping, [TIMESTAMP, 10], 5, chart); + const winner = result.find((series) => series.isClosestToCursor); + expect(winner?.seriesName).toBe('series-a'); + }); + + it('downplays previously-hovered non-candidate series (fix for persistent emphasis)', () => { + const data: TimeSeries[] = [ + { name: 'a', values: [[TIMESTAMP, 1]] }, + { name: 'b', values: [[TIMESTAMP, 2]] }, + { name: 'c', values: [[TIMESTAMP, 3]] }, + ]; + const seriesMapping = [ + { type: 'line', name: 'a', color: '#111', id: 'a' }, + { type: 'line', name: 'b', color: '#222', id: 'b' }, + { type: 'line', name: 'c', color: '#333', id: 'c' }, + ] as unknown as TimeChartSeriesMapping; + + const { chart, dispatched } = buildChartMock(); + + const result = checkforNearbyTimeSeries(data, seriesMapping, [TIMESTAMP, 1000], 0.5, chart); + + expect(result).toEqual([]); + + const downplayed = new Set(); + for (const action of dispatched) { + if (action.type !== 'downplay') continue; + if (Array.isArray(action.seriesIndex)) { + for (const idx of action.seriesIndex) downplayed.add(idx); + } else if (typeof action.seriesIndex === 'number') { + downplayed.add(action.seriesIndex); + } + } + expect(downplayed.has(0)).toBe(true); + expect(downplayed.has(1)).toBe(true); + expect(downplayed.has(2)).toBe(true); + }); +}); diff --git a/components/src/TimeSeriesTooltip/nearby-series.ts b/components/src/TimeSeriesTooltip/nearby-series.ts index 6764f67d..134f8797 100644 --- a/components/src/TimeSeriesTooltip/nearby-series.ts +++ b/components/src/TimeSeriesTooltip/nearby-series.ts @@ -12,7 +12,7 @@ // limitations under the License. import { ECharts as EChartsInstance } from 'echarts/core'; -import { LineSeriesOption } from 'echarts/charts'; +import { BarSeriesOption } from 'echarts/charts'; import { TimeSeries, TimeSeriesValueTuple } from '@perses-dev/spec'; import { EChartsDataFormat, @@ -24,25 +24,273 @@ import { } from '../model'; import { batchDispatchNearbySeriesActions, getPointInGrid, getClosestTimestamp } from '../utils'; import { CursorCoordinates, CursorData, EMPTY_TOOLTIP_DATA } from './tooltip-model'; +import { + calculateBarBandwidth, + calculateBarSegmentBounds, + calculateBarYBounds, + calculateVisualYForSeries, + getPixelXFromGrid, +} from './utils'; +import { Candidate, GetYBufferParams, IsWithinPercentageRangeParams, NearbySeriesArray } from './types'; + +export type { NearbySeriesArray, NearbySeriesInfo } from './types'; // increase multipliers to show more series in tooltip export const INCREASE_NEARBY_SERIES_MULTIPLIER = 5.5; // adjusts how many series show in tooltip (higher == more series shown) export const DYNAMIC_NEARBY_SERIES_MULTIPLIER = 30; // used for adjustment after series number divisor export const SHOW_FEWER_SERIES_LIMIT = 5; -export interface NearbySeriesInfo { - seriesIdx: number | null; - datumIdx: number | null; - seriesName: string; - date: number; - markerColor: string; - x: number; - y: number; - formattedY: string; - isClosestToCursor: boolean; +function gatherCandidates( + data: TimeSeries[], + seriesMapping: TimeChartSeriesMapping, + closestTimestamp: number, + cursorX: number, + cursorY: number, + cursorXPixel: number | null, + cursorPixelY: number | undefined, + yBuffer: number, + yBufferPixels: number | null, + chart: EChartsInstance +): Candidate[] { + const candidates: Candidate[] = []; + const totalSeries = data.length; + + const stackTotals = new Map(); + + let sortedTimestamps: number[] = []; + const firstValues = data[0]?.values; + if (firstValues && firstValues.length > 0) { + const seen = new Set(); + for (const [ts] of firstValues) { + if (!seen.has(ts)) { + seen.add(ts); + sortedTimestamps.push(ts); + } + } + sortedTimestamps = sortedTimestamps.sort((a, b) => a - b); + } + + // Bar-only indexes: ECharts groups bars independently of lines, so bar-relative index and count must exclude line series. + const barSeriesIndexes: number[] = []; + for (let i = 0; i < totalSeries; i++) { + if ((seriesMapping[i]?.type ?? 'line') === 'bar') barSeriesIndexes.push(i); + } + + // Computed once outside the loop — both depend only on the timestamp, not the series index. + let barBandwidth: number | null = null; + let barCenterPixelX: number | null = null; + if (barSeriesIndexes.length > 0 && cursorXPixel !== null) { + barBandwidth = calculateBarBandwidth(closestTimestamp, sortedTimestamps, chart); + barCenterPixelX = getPixelXFromGrid(closestTimestamp, chart); + } + + for (let seriesIdx = 0; seriesIdx < totalSeries; seriesIdx++) { + const currentSeries = seriesMapping[seriesIdx]; + if (!currentSeries) continue; + + const currentDataset = data[seriesIdx]; + if (!currentDataset) continue; + + const currentDatasetValues: TimeSeriesValueTuple[] | undefined = currentDataset.values; + if (!currentDatasetValues || !Array.isArray(currentDatasetValues)) continue; + + const seriesType = currentSeries.type ?? 'line'; + const currentSeriesName = currentSeries.name ? currentSeries.name.toString() : ''; + const seriesId = currentSeries.id ? currentSeries.id.toString() : ''; + const markerColor = (currentSeries.color ?? '#000').toString(); + + let datumIdx = -1; + let xValue = 0; + let yValue: number | null | undefined; + for (let i = 0; i < currentDatasetValues.length; i++) { + const tuple = currentDatasetValues[i]; + if (!tuple) continue; + if (tuple[0] === closestTimestamp) { + datumIdx = i; + xValue = tuple[0]; + yValue = tuple[1]; + break; + } + } + if (datumIdx === -1) continue; + + if (yValue === null || yValue === undefined) continue; + + let isCandidate = false; + let visualY = yValue; + let distance = Infinity; + + if (seriesType === 'line') { + visualY = calculateVisualYForSeries(seriesIdx, yValue, seriesMapping, stackTotals); + + if (cursorPixelY !== undefined && yBufferPixels !== null) { + try { + const dataPointPixel = chart.convertToPixel({ seriesIndex: seriesIdx }, [datumIdx, visualY]); + if (dataPointPixel && dataPointPixel[1] !== undefined) { + const pixelDistance = Math.abs(cursorPixelY - dataPointPixel[1]); + isCandidate = pixelDistance <= yBufferPixels; + distance = pixelDistance; + } else { + const verticalDistance = Math.abs(visualY - cursorY); + isCandidate = verticalDistance <= yBuffer; + distance = verticalDistance; + } + } catch { + const verticalDistance = Math.abs(visualY - cursorY); + isCandidate = verticalDistance <= yBuffer; + distance = verticalDistance; + } + } else { + const verticalDistance = Math.abs(visualY - cursorY); + isCandidate = verticalDistance <= yBuffer; + distance = verticalDistance; + } + } else if (seriesType === 'bar') { + if (cursorXPixel === null || barBandwidth === null || barCenterPixelX === null) continue; + + const barRelativeIdx = barSeriesIndexes.indexOf(seriesIdx); + if (barRelativeIdx === -1) continue; + + const segmentBounds = calculateBarSegmentBounds( + barRelativeIdx, + barBandwidth, + barCenterPixelX, + barSeriesIndexes.length + ); + + const isWithinXBounds = cursorXPixel >= segmentBounds.left && cursorXPixel <= segmentBounds.right; + if (!isWithinXBounds) continue; + + const stackId = (currentSeries as BarSeriesOption).stack; + let isHoveringYBounds = true; + + if (stackId) { + const stackIdStr = stackId.toString(); + const visualYBottom = stackTotals.get(stackIdStr) ?? 0; + visualY = calculateVisualYForSeries(seriesIdx, yValue, seriesMapping, stackTotals); + const yBounds = calculateBarYBounds(visualYBottom, visualY, chart); + + if (yBounds) { + const cursorYPixel = chart.convertToPixel('grid', [0, cursorY]); + if (cursorYPixel && cursorYPixel[1] !== undefined) { + isHoveringYBounds = cursorYPixel[1] >= yBounds.top && cursorYPixel[1] <= yBounds.bottom; + } + } + } else { + visualY = yValue; + } + + if (!isHoveringYBounds) continue; + + const segmentCenter = (segmentBounds.left + segmentBounds.right) / 2; + distance = Math.abs(cursorXPixel - segmentCenter); + isCandidate = true; + } + + if (isCandidate) { + candidates.push({ + seriesIdx, + datumIdx, + seriesId, + seriesName: currentSeriesName, + date: closestTimestamp, + markerColor, + x: xValue, + y: yValue, + visualY, + distance, + }); + } + } + + return candidates; } -export type NearbySeriesArray = NearbySeriesInfo[]; +function findClosestCandidate(candidates: Candidate[]): Candidate | null { + if (candidates.length === 0) return null; + let winner: Candidate | null = null; + for (const candidate of candidates) { + if (winner === null || candidate.distance < winner.distance) { + winner = candidate; + } + } + return winner; +} + +function processCandidates( + candidates: Candidate[], + winner: Candidate | null, + format: FormatOptions | undefined, + seriesFormatMap: Map | undefined, + chart: EChartsInstance, + nonCandidateSeriesIndexes: number[] +): NearbySeriesArray { + const nearbySeriesIndexes: number[] = []; + const emphasizedSeriesIndexes: number[] = []; + const nonEmphasizedSeriesIndexes: number[] = [...nonCandidateSeriesIndexes]; + const emphasizedDatapoints: DatapointInfo[] = []; + const duplicateDatapoints: DatapointInfo[] = []; + const yValueCounts: Map = new Map(); + + const result: NearbySeriesArray = []; + + for (const candidate of candidates) { + const seriesFormat = seriesFormatMap?.get(candidate.seriesId) ?? format; + // Use raw y, not visualY — visualY is for proximity detection only. + const displayY = candidate.y; + const formattedY = formatValue(displayY, seriesFormat); + const isClosestToCursor = winner !== null && candidate.seriesIdx === winner.seriesIdx; + + if (isClosestToCursor) { + emphasizedSeriesIndexes.push(candidate.seriesIdx); + + const duplicateValuesCount = yValueCounts.get(displayY) ?? 0; + yValueCounts.set(displayY, duplicateValuesCount + 1); + if (duplicateValuesCount > 0) { + duplicateDatapoints.push({ + seriesIndex: candidate.seriesIdx, + dataIndex: candidate.datumIdx, + seriesName: candidate.seriesName, + yValue: displayY, + }); + } + + emphasizedDatapoints.push({ + seriesIndex: candidate.seriesIdx, + dataIndex: candidate.datumIdx, + seriesName: candidate.seriesName, + yValue: displayY, + }); + } else { + nonEmphasizedSeriesIndexes.push(candidate.seriesIdx); + } + + result.push({ + seriesIdx: candidate.seriesIdx, + datumIdx: candidate.datumIdx, + seriesName: candidate.seriesName, + date: candidate.date, + x: candidate.x, + y: displayY, + formattedY, + markerColor: candidate.markerColor, + isClosestToCursor, + }); + + nearbySeriesIndexes.push(candidate.seriesIdx); + } + + batchDispatchNearbySeriesActions( + chart, + nearbySeriesIndexes, + emphasizedSeriesIndexes, + nonEmphasizedSeriesIndexes, + emphasizedDatapoints, + duplicateDatapoints + ); + + return result; +} /** * Returns formatted series data for the points that are close to the user's cursor. @@ -57,40 +305,23 @@ export function checkforNearbyTimeSeries( format?: FormatOptions, seriesFormatMap?: Map, // in the case of multi-axis, we need the cursor Y position in pixel space - cursorPixelY?: number + cursorPixelY?: number, + cursorXPixel?: number | null ): NearbySeriesArray { - const currentNearbySeriesData: NearbySeriesArray = []; const cursorX: number | null = pointInGrid[0] ?? null; const cursorY: number | null = pointInGrid[1] ?? null; - if (cursorX === null || cursorY === null) return currentNearbySeriesData; + if (cursorX === null || cursorY === null) return EMPTY_TOOLTIP_DATA; + if (chart.dispatchAction === undefined) return EMPTY_TOOLTIP_DATA; + if (!Array.isArray(data)) return EMPTY_TOOLTIP_DATA; - if (chart.dispatchAction === undefined) return currentNearbySeriesData; - - if (!Array.isArray(data)) return currentNearbySeriesData; - const nearbySeriesIndexes: number[] = []; - const emphasizedSeriesIndexes: number[] = []; - const nonEmphasizedSeriesIndexes: number[] = []; - const emphasizedDatapoints: DatapointInfo[] = []; - const duplicateDatapoints: DatapointInfo[] = []; - - const totalSeries = data.length; - - const yValueCounts: Map = new Map(); - - // Only need to loop through first dataset source since getCommonTimeScale ensures xAxis timestamps are consistent + // All series share the same x-axis timestamps (enforced by getCommonTimeScale). const firstTimeSeriesValues = data[0]?.values; const closestTimestamp = getClosestTimestamp(firstTimeSeriesValues, cursorX); + if (closestTimestamp === null) return EMPTY_TOOLTIP_DATA; - if (closestTimestamp === null) { - return EMPTY_TOOLTIP_DATA; - } - - // For multi-axis support: convert yBuffer to pixel space for consistent comparison - // This allows us to compare series on different Y axes fairly let yBufferPixels: number | null = null; if (cursorPixelY !== undefined) { - // Convert a point at cursorY and cursorY + yBuffer to pixels to get the buffer in pixel space const cursorPoint = chart.convertToPixel('grid', [0, cursorY]); const bufferPoint = chart.convertToPixel('grid', [0, cursorY + yBuffer]); if (cursorPoint && bufferPoint && cursorPoint[1] !== undefined && bufferPoint[1] !== undefined) { @@ -98,143 +329,31 @@ export function checkforNearbyTimeSeries( } } - // find the timestamp with data that is closest to cursorX - for (let seriesIdx = 0; seriesIdx < totalSeries; seriesIdx++) { - const currentSeries = seriesMapping[seriesIdx]; - if (!currentSeries) break; - - const currentDataset = totalSeries > 0 ? data[seriesIdx] : null; - if (!currentDataset) break; - - const currentDatasetValues: TimeSeriesValueTuple[] = currentDataset.values; - if (currentDatasetValues === undefined || !Array.isArray(currentDatasetValues)) break; - const lineSeries = currentSeries as LineSeriesOption; - const currentSeriesName = lineSeries.name ? lineSeries.name.toString() : ''; - const seriesId = lineSeries.id ? lineSeries.id.toString() : ''; - const markerColor = lineSeries.color ?? '#000'; - - // Get the format for this series (from seriesFormatMap or fallback to default format) - const seriesFormat = seriesFormatMap?.get(seriesId) ?? format; - - if (Array.isArray(data)) { - for (let datumIdx = 0; datumIdx < currentDatasetValues.length; datumIdx++) { - const nearbyTimeSeries = currentDatasetValues[datumIdx]; - if (nearbyTimeSeries === undefined || !Array.isArray(nearbyTimeSeries)) break; - - const xValue = nearbyTimeSeries[0]; - const yValue = nearbyTimeSeries[1]; - // TODO: ensure null values not displayed in tooltip - if (yValue !== undefined && yValue !== null) { - if (closestTimestamp === xValue) { - // Check if this series is nearby the cursor - let isNearby = false; - - // For multi-axis: compare in pixel space - if (cursorPixelY !== undefined && yBufferPixels !== null) { - const dataPointPixel = chart.convertToPixel({ seriesIndex: seriesIdx }, [datumIdx, yValue]); - if (dataPointPixel && dataPointPixel[1] !== undefined) { - const pixelDistance = Math.abs(cursorPixelY - dataPointPixel[1]); - isNearby = pixelDistance <= yBufferPixels; - } else { - // Fallback to data-space comparison for primary axis - isNearby = cursorY <= yValue + yBuffer && cursorY >= yValue - yBuffer; - } - } else { - // Fallback to original data-space comparison - isNearby = cursorY <= yValue + yBuffer && cursorY >= yValue - yBuffer; - } - - if (isNearby) { - // show fewer bold series in tooltip when many total series - const minPercentRange = totalSeries > SHOW_FEWER_SERIES_LIMIT ? 2 : 5; - const percentRangeToCheck = Math.max(minPercentRange, 100 / totalSeries); - - // For isClosestToCursor, also use pixel space for multi-axis - let isClosestToCursor = false; - if (cursorPixelY !== undefined) { - const dataPointPixel = chart.convertToPixel({ seriesIndex: seriesIdx }, [datumIdx, yValue]); - if (dataPointPixel && dataPointPixel[1] !== undefined) { - const pixelDistance = Math.abs(cursorPixelY - dataPointPixel[1]); - // Use percentage of buffer for "closest" determination - const tightBufferPixels = (yBufferPixels ?? 50) * (percentRangeToCheck / 100); - isClosestToCursor = pixelDistance <= Math.max(tightBufferPixels, 5); - } else { - isClosestToCursor = isWithinPercentageRange({ - valueToCheck: cursorY, - baseValue: yValue, - percentage: percentRangeToCheck, - }); - } - } else { - isClosestToCursor = isWithinPercentageRange({ - valueToCheck: cursorY, - baseValue: yValue, - percentage: percentRangeToCheck, - }); - } - - if (isClosestToCursor) { - // shows as bold in tooltip, customize 'emphasis' options in getTimeSeries util - emphasizedSeriesIndexes.push(seriesIdx); + const resolvedCursorXPixel = cursorXPixel ?? getPixelXFromGrid(closestTimestamp, chart); + + const candidates = gatherCandidates( + data, + seriesMapping, + closestTimestamp, + cursorX, + cursorY, + resolvedCursorXPixel, + cursorPixelY, + yBuffer, + yBufferPixels, + chart + ); - // Used to determine which datapoint to apply select styles to. - // Accounts for cases where lines may be rendered directly on top of eachother. - const duplicateValuesCount = yValueCounts.get(yValue) ?? 0; - yValueCounts.set(yValue, duplicateValuesCount + 1); - if (duplicateValuesCount > 0) { - duplicateDatapoints.push({ - seriesIndex: seriesIdx, - dataIndex: datumIdx, - seriesName: currentSeriesName, - yValue: yValue, - }); - } + const winner = findClosestCandidate(candidates); - // keep track of all bold datapoints in tooltip so that 'select' state only applied to topmost - emphasizedDatapoints.push({ - seriesIndex: seriesIdx, - dataIndex: datumIdx, - seriesName: currentSeriesName, - yValue: yValue, - }); - } else { - nonEmphasizedSeriesIndexes.push(seriesIdx); - // ensure series far away from cursor are not highlighted - chart.dispatchAction({ - type: 'downplay', - seriesIndex: seriesIdx, - }); - } - const formattedY = formatValue(yValue, seriesFormat); - currentNearbySeriesData.push({ - seriesIdx: seriesIdx, - datumIdx: datumIdx, - seriesName: currentSeriesName, - date: closestTimestamp, - x: xValue, - y: yValue, - formattedY: formattedY, - markerColor: markerColor.toString(), - isClosestToCursor, - }); - nearbySeriesIndexes.push(seriesIdx); - } - } - } - } - } + const candidateIndexes = new Set(); + for (const candidate of candidates) candidateIndexes.add(candidate.seriesIdx); + const nonCandidateSeriesIndexes: number[] = []; + for (let idx = 0; idx < data.length; idx++) { + if (!candidateIndexes.has(idx)) nonCandidateSeriesIndexes.push(idx); } - batchDispatchNearbySeriesActions( - chart, - nearbySeriesIndexes, - emphasizedSeriesIndexes, - nonEmphasizedSeriesIndexes, - emphasizedDatapoints, - duplicateDatapoints - ); - - return currentNearbySeriesData; + return processCandidates(candidates, winner, format, seriesFormatMap, chart, nonCandidateSeriesIndexes); } /** @@ -396,31 +515,28 @@ export function getNearbySeriesData({ if (cursorTargetMatchesChart === false || data === null || chart['_model'] === undefined) return EMPTY_TOOLTIP_DATA; - // mousemove position undefined when not hovering over chart canvas if (mousePos.plotCanvas.x === undefined || mousePos.plotCanvas.y === undefined) return EMPTY_TOOLTIP_DATA; const cursorPixelY = mousePos.plotCanvas.y; - const pointInGrid = getPointInGrid(mousePos.plotCanvas.x, cursorPixelY, chart); + const cursorXPixel = mousePos.plotCanvas.x; + const pointInGrid = getPointInGrid(cursorXPixel, cursorPixelY, chart); if (pointInGrid !== null) { const chartModel = chart['_model']; const yAxisScale = chartModel.getComponent('yAxis').axis.scale; const isLogScale = yAxisScale.type === 'log'; let yInterval = yAxisScale._interval; - // For logarithmic scales, convert the log interval to actual data range + // For log scales, convert from log-space extent to actual data range and use 1% as the interval. if (isLogScale && yAxisScale.base) { const logBase = yAxisScale.base; const extent = yAxisScale._extent; - // Calculate actual data range from log extent - // extent is in log space (e.g., [0, 2] for 10^0 to 10^2) + // e.g. extent [0, 2] → 10^0..10^2 const actualMin = logBase ** extent[0]; const actualMax = logBase ** extent[1]; - // Use a fraction of the actual range as the interval yInterval = (actualMax - actualMin) / 100; } const totalSeries = data.length; const yBuffer = getYBuffer({ yInterval, totalSeries, showAllSeries }); - // Detect if chart has multiple Y-axes by checking if any series uses yAxisIndex > 0 const hasMultipleYAxes = seriesMapping.some((series) => series.yAxisIndex !== undefined && series.yAxisIndex > 0); return checkforNearbyTimeSeries( @@ -431,7 +547,8 @@ export function getNearbySeriesData({ chart, format, seriesFormatMap, - hasMultipleYAxes ? cursorPixelY : undefined + hasMultipleYAxes ? cursorPixelY : undefined, + cursorXPixel ); } @@ -446,11 +563,7 @@ export function isWithinPercentageRange({ valueToCheck, baseValue, percentage, -}: { - valueToCheck: number; - baseValue: number; - percentage: number; -}): boolean { +}: IsWithinPercentageRangeParams): boolean { const range = (percentage / 100) * baseValue; const lowerBound = baseValue - range; const upperBound = baseValue + range; @@ -460,15 +573,7 @@ export function isWithinPercentageRange({ /* * Get range to check within for nearby series to show in tooltip. */ -export function getYBuffer({ - yInterval, - totalSeries, - showAllSeries = false, -}: { - yInterval: number; - totalSeries: number; - showAllSeries?: boolean; -}): number { +export function getYBuffer({ yInterval, totalSeries, showAllSeries = false }: GetYBufferParams): number { if (showAllSeries) { return yInterval * 10; // roughly correlates with grid so entire canvas is searched } diff --git a/components/src/TimeSeriesTooltip/tooltip-model.ts b/components/src/TimeSeriesTooltip/tooltip-model.ts index 9b819328..d10ab3d6 100644 --- a/components/src/TimeSeriesTooltip/tooltip-model.ts +++ b/components/src/TimeSeriesTooltip/tooltip-model.ts @@ -12,7 +12,7 @@ // limitations under the License. import { useEffect, useState } from 'react'; -import { NearbySeriesArray } from './nearby-series'; +import { NearbySeriesArray } from './types'; export const TOOLTIP_MIN_WIDTH = 375; export const TOOLTIP_MAX_WIDTH = 650; diff --git a/components/src/TimeSeriesTooltip/types.ts b/components/src/TimeSeriesTooltip/types.ts new file mode 100644 index 00000000..d3c75154 --- /dev/null +++ b/components/src/TimeSeriesTooltip/types.ts @@ -0,0 +1,46 @@ +// Copyright 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 NearbySeriesInfo { + seriesIdx: number | null; + datumIdx: number | null; + seriesName: string; + date: number; + markerColor: string; + x: number; + y: number; + formattedY: string; + isClosestToCursor: boolean; +} + +export type NearbySeriesArray = NearbySeriesInfo[]; + +export type Candidate = Omit & { + seriesIdx: number; + datumIdx: number; + seriesId: string; + visualY: number; + distance: number; +}; + +export type IsWithinPercentageRangeParams = { + valueToCheck: number; + baseValue: number; + percentage: number; +}; + +export type GetYBufferParams = { + yInterval: number; + totalSeries: number; + showAllSeries?: boolean; +}; diff --git a/components/src/TimeSeriesTooltip/utils.test.ts b/components/src/TimeSeriesTooltip/utils.test.ts new file mode 100644 index 00000000..29661f58 --- /dev/null +++ b/components/src/TimeSeriesTooltip/utils.test.ts @@ -0,0 +1,158 @@ +// Copyright 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 { assembleTransform } from './utils'; +import { CursorData, TOOLTIP_MAX_HEIGHT, TOOLTIP_MAX_WIDTH } from './tooltip-model'; + +const VIEWPORT_WIDTH = 1600; +const VIEWPORT_HEIGHT = 720; + +function makeMousePos(pageX: number, pageY: number): CursorData['coords'] { + return { + page: { x: pageX, y: pageY }, + client: { x: pageX, y: pageY }, + plotCanvas: { x: pageX, y: pageY }, + target: null, + }; +} + +function parseTransform(transform: string | undefined): { x: number; y: number } | null { + if (!transform) return null; + const match = transform.match(/translate3d\((-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px,\s*0(?:px)?\)/); + if (!match || match[1] === undefined || match[2] === undefined) return null; + return { x: parseFloat(match[1]), y: parseFloat(match[2]) }; +} + +describe('assembleTransform', () => { + const originalInnerWidth = window.innerWidth; + const originalInnerHeight = window.innerHeight; + const originalScrollY = window.scrollY; + + beforeAll(() => { + Object.defineProperty(window, 'innerWidth', { configurable: true, value: VIEWPORT_WIDTH }); + Object.defineProperty(window, 'innerHeight', { configurable: true, value: VIEWPORT_HEIGHT }); + Object.defineProperty(window, 'scrollY', { configurable: true, value: 0 }); + }); + + afterAll(() => { + Object.defineProperty(window, 'innerWidth', { configurable: true, value: originalInnerWidth }); + Object.defineProperty(window, 'innerHeight', { configurable: true, value: originalInnerHeight }); + Object.defineProperty(window, 'scrollY', { configurable: true, value: originalScrollY }); + }); + + it('returns undefined when mousePos is null', () => { + expect(assembleTransform(null, null, 200, 400)).toBeUndefined(); + }); + + describe('when tooltip size is unknown (first render)', () => { + it('uses TOOLTIP_MAX_HEIGHT as fallback and clamps against the viewport bottom', () => { + const mousePos = makeMousePos(800, 400); + const result = parseTransform(assembleTransform(mousePos, null, 0, 0)); + expect(result).not.toBeNull(); + expect(result!.y).toBe(VIEWPORT_HEIGHT - TOOLTIP_MAX_HEIGHT - 16); + // Worst-case bottom (y + max height) must still fit inside the viewport. + expect(result!.y + TOOLTIP_MAX_HEIGHT).toBeLessThanOrEqual(VIEWPORT_HEIGHT); + }); + + it('uses TOOLTIP_MAX_WIDTH as fallback and flips to the left of the cursor when needed', () => { + const mousePos = makeMousePos(VIEWPORT_WIDTH - 100, 100); + const result = parseTransform(assembleTransform(mousePos, null, 0, 0)); + expect(result).not.toBeNull(); + expect(result!.x).toBe(VIEWPORT_WIDTH - 100 - TOOLTIP_MAX_WIDTH - 32); + }); + }); + + describe('when tooltip size is known', () => { + it('places the tooltip to the right and below the cursor when it fits', () => { + const mousePos = makeMousePos(400, 100); + const result = parseTransform(assembleTransform(mousePos, null, 200, 300)); + expect(result).toEqual({ x: 400 + 32, y: 100 + 16 }); + }); + + it('clamps y so the tooltip does not extend past the viewport bottom', () => { + const mousePos = makeMousePos(400, 600); + const result = parseTransform(assembleTransform(mousePos, null, 400, 300)); + expect(result).not.toBeNull(); + expect(result!.y).toBe(VIEWPORT_HEIGHT - 400 - 16); + expect(result!.y + 400).toBeLessThanOrEqual(VIEWPORT_HEIGHT); + }); + + it('flips the tooltip to the left of the cursor when it would overflow the right edge', () => { + const mousePos = makeMousePos(VIEWPORT_WIDTH - 100, 100); + const result = parseTransform(assembleTransform(mousePos, null, 200, 400)); + expect(result).not.toBeNull(); + expect(result!.x).toBe(VIEWPORT_WIDTH - 100 - 400 - 32); + }); + + it('never places the tooltip past the left edge of the viewport', () => { + const mousePos = makeMousePos(0, 100); + const result = parseTransform(assembleTransform(mousePos, null, 200, 3000)); + expect(result).not.toBeNull(); + expect(result!.x).toBe(32); + }); + + it('never places the tooltip past the top of the viewport', () => { + const mousePos = makeMousePos(400, -100); + const result = parseTransform(assembleTransform(mousePos, null, 200, 400)); + expect(result).not.toBeNull(); + expect(result!.y).toBe(4); + }); + }); + + describe('when a pinnedPos is provided', () => { + it('uses pinnedPos instead of the live mouse position', () => { + const mousePos = makeMousePos(400, 100); + const pinnedPos = { + page: { x: 800, y: 200 }, + client: { x: 800, y: 200 }, + plotCanvas: { x: 800, y: 200 }, + target: null, + }; + const result = parseTransform(assembleTransform(mousePos, pinnedPos, 200, 300)); + expect(result).toEqual({ x: 832, y: 216 }); + }); + }); + + describe('when a container element is provided', () => { + it('adjusts coordinates relative to the container', () => { + const container = { + getBoundingClientRect: () => + ({ top: 200, left: 100, width: 800, height: 400, right: 900, bottom: 600, x: 100, y: 200 }) as DOMRect, + scrollLeft: 0, + scrollTop: 0, + scrollHeight: 400, + } as unknown as Element; + + // Cursor at page (500, 250) → relative (400, 50); tooltip 100 tall fits inside container. + const mousePos = makeMousePos(500, 250); + const result = parseTransform(assembleTransform(mousePos, null, 100, 200, container)); + expect(result).not.toBeNull(); + expect(result).toEqual({ x: 432, y: 66 }); + }); + + it('falls back to TOOLTIP_MAX_HEIGHT when tooltip size is unknown inside a container', () => { + const container = { + getBoundingClientRect: () => + ({ top: 0, left: 0, width: 800, height: 400, right: 800, bottom: 400, x: 0, y: 0 }) as DOMRect, + scrollLeft: 0, + scrollTop: 0, + scrollHeight: 400, + } as unknown as Element; + + const mousePos = makeMousePos(400, 300); + const result = parseTransform(assembleTransform(mousePos, null, 0, 0, container)); + expect(result).not.toBeNull(); + expect(result!.y).toBe(4); + }); + }); +}); diff --git a/components/src/TimeSeriesTooltip/utils.ts b/components/src/TimeSeriesTooltip/utils.ts index 18f5cd69..40398dfe 100644 --- a/components/src/TimeSeriesTooltip/utils.ts +++ b/components/src/TimeSeriesTooltip/utils.ts @@ -12,6 +12,9 @@ // limitations under the License. import { Theme } from '@mui/material'; +import { ECharts as EChartsInstance } from 'echarts/core'; +import { BarSeriesOption, LineSeriesOption } from 'echarts/charts'; +import { TimeChartSeriesMapping } from '../model'; import { CursorCoordinates, CursorData, @@ -23,7 +26,7 @@ import { } from './tooltip-model'; /** - * Determine position of tooltip depending on chart dimensions and the number of focused series + * Determine position of tooltip depending on chart dimensions and the number of focused series. */ export function assembleTransform( mousePos: CursorData['coords'], @@ -45,6 +48,10 @@ export function assembleTransform( if (mousePos.plotCanvas.x === undefined) return undefined; + // Fall back to max size before the resize observer reports a real measurement. + const effectiveHeight = tooltipHeight > 0 ? tooltipHeight : TOOLTIP_MAX_HEIGHT; + const effectiveWidth = tooltipWidth > 0 ? tooltipWidth : TOOLTIP_MAX_WIDTH; + let x = mousePos.page.x + cursorPaddingX; // Default to right side of the cursor let y = mousePos.page.y + cursorPaddingY; @@ -56,19 +63,19 @@ export function assembleTransform( // Ensure tooltip does not go out of the container's bottom const containerBottom = containerRect.top + containerElement.scrollHeight; - if (y + tooltipHeight > containerBottom) { - y = Math.max(containerBottom - tooltipHeight - cursorPaddingY, TOOLTIP_PADDING / 2); + if (y + effectiveHeight > containerBottom) { + y = Math.max(containerBottom - effectiveHeight - cursorPaddingY, TOOLTIP_PADDING / 2); } } else { // Ensure tooltip does not go out of the screen on the bottom - if (y + tooltipHeight > window.innerHeight + window.scrollY) { - y = Math.max(window.innerHeight + window.scrollY - tooltipHeight - cursorPaddingY, TOOLTIP_PADDING / 2); + if (y + effectiveHeight > window.innerHeight + window.scrollY) { + y = Math.max(window.innerHeight + window.scrollY - effectiveHeight - cursorPaddingY, TOOLTIP_PADDING / 2); } } // Ensure tooltip does not go out of the screen on the right - if (x + tooltipWidth > window.innerWidth) { - x = mousePos.page.x - tooltipWidth - cursorPaddingX; // Move to the left of the cursor + if (x + effectiveWidth > window.innerWidth) { + x = mousePos.page.x - effectiveWidth - cursorPaddingX; // Move to the left of the cursor } // Ensure tooltip does not go out of the screen on the left @@ -107,8 +114,9 @@ export function getTooltipStyles( fontSize: '11px', visibility: 'visible', opacity: 1, - transition: 'all 0.1s ease-out', - // Ensure pinned tooltip shows behind edit panel drawer and sticky header + // Animating transform causes intermediate positions outside the viewport; animate opacity/visibility instead. + transition: 'opacity 0.1s ease-out, visibility 0.1s ease-out', + // Pinned tooltip should not float above the drawer/sticky header. zIndex: pinnedPos !== null ? 'auto' : theme.zIndex.tooltip, overflow: 'hidden', '&:hover': { @@ -116,3 +124,116 @@ export function getTooltipStyles( }, }; } + +export function getPixelXFromGrid(timestamp: number, chart: EChartsInstance): number | null { + try { + const pixelCoords = chart.convertToPixel('grid', [timestamp, 0]); + return pixelCoords?.[0] ?? null; + } catch { + return null; + } +} + +/** + * Returns the cumulative (visual) Y for a series, accumulating stack totals in-place. + * For non-stacked series, returns the raw yValue unchanged. + * Mutates `stackTotals` — pass a fresh Map for each cursor evaluation. + */ +export function calculateVisualYForSeries( + seriesIdx: number, + yValue: number, + seriesMapping: TimeChartSeriesMapping, + stackTotals: Map +): number { + const currentSeries = seriesMapping[seriesIdx]; + if (!currentSeries) return yValue; + + const stackId = (currentSeries as LineSeriesOption | BarSeriesOption).stack; + if (!stackId) { + return yValue; + } + + const stackIdStr = stackId.toString(); + const currentTotal = stackTotals.get(stackIdStr) ?? 0; + const newTotal = currentTotal + yValue; + stackTotals.set(stackIdStr, newTotal); + return newTotal; +} + +export function calculateBarBandwidth(timestamp: number, sortedTimestamps: number[], chart: EChartsInstance): number { + const currentIdx = sortedTimestamps.indexOf(timestamp); + if (currentIdx === -1) { + return 20; + } + + const prevTimestamp = currentIdx > 0 ? (sortedTimestamps[currentIdx - 1] ?? null) : null; + const nextTimestamp = currentIdx < sortedTimestamps.length - 1 ? (sortedTimestamps[currentIdx + 1] ?? null) : null; + + const currentPixelX = getPixelXFromGrid(timestamp, chart); + if (currentPixelX === null) return 20; + + let leftBound: number; + let rightBound: number; + + if (prevTimestamp !== null && nextTimestamp !== null) { + const prevPixelX = getPixelXFromGrid(prevTimestamp, chart) ?? currentPixelX; + const nextPixelX = getPixelXFromGrid(nextTimestamp, chart) ?? currentPixelX; + leftBound = (currentPixelX + prevPixelX) / 2; + rightBound = (currentPixelX + nextPixelX) / 2; + } else if (prevTimestamp !== null) { + const prevPixelX = getPixelXFromGrid(prevTimestamp, chart) ?? currentPixelX; + leftBound = (currentPixelX + prevPixelX) / 2; + rightBound = currentPixelX + (currentPixelX - leftBound); + } else if (nextTimestamp !== null) { + const nextPixelX = getPixelXFromGrid(nextTimestamp, chart) ?? currentPixelX; + rightBound = (currentPixelX + nextPixelX) / 2; + leftBound = currentPixelX - (rightBound - currentPixelX); + } else { + return 20; + } + + return Math.max(1, rightBound - leftBound); +} + +/** + * Computes the pixel left/right bounds of one bar segment within a group. + * @param barRelativeIdx - zero-based index among bar-only series + * @param bandwidth - total pixel width for the bar group + * @param centerPixelX - pixel X of the bar group centre + * @param barCount - total bar series count (lines excluded) + */ +export function calculateBarSegmentBounds( + barRelativeIdx: number, + bandwidth: number, + centerPixelX: number, + barCount: number +): { left: number; right: number } { + const count = Math.max(1, barCount); + const segmentWidth = bandwidth / count; + const segmentLeft = centerPixelX - bandwidth / 2 + barRelativeIdx * segmentWidth; + return { + left: segmentLeft, + right: segmentLeft + segmentWidth, + }; +} + +export function calculateBarYBounds( + visualYBottom: number, + visualYTop: number, + chart: EChartsInstance +): { top: number; bottom: number } | null { + try { + const bottomPixel = chart.convertToPixel('grid', [0, visualYBottom]); + const topPixel = chart.convertToPixel('grid', [0, visualYTop]); + + if (!bottomPixel || !topPixel || bottomPixel[1] === undefined || topPixel[1] === undefined) return null; + + // Y increases downward in pixels; min/max normalizes the mapping for negative bar values. + return { + top: Math.min(topPixel[1], bottomPixel[1]), + bottom: Math.max(topPixel[1], bottomPixel[1]), + }; + } catch { + return null; + } +} diff --git a/components/src/utils/chart-actions.test.ts b/components/src/utils/chart-actions.test.ts index ca3e627d..cb555adf 100644 --- a/components/src/utils/chart-actions.test.ts +++ b/components/src/utils/chart-actions.test.ts @@ -12,7 +12,13 @@ // limitations under the License. import { TimeSeries, TimeSeriesValueTuple } from '@perses-dev/spec'; -import { getClosestTimestamp, getClosestTimestampInFullDataset } from './chart-actions'; +import { ECharts as EChartsInstance } from 'echarts/core'; +import { DatapointInfo } from '../model'; +import { + batchDispatchNearbySeriesActions, + getClosestTimestamp, + getClosestTimestampInFullDataset, +} from './chart-actions'; const TEST_TIME_SERIES_VALUES: TimeSeriesValueTuple[] = [ [1690381125000, 0.12], @@ -138,3 +144,70 @@ describe('getClosestTimestampInFullDataset', () => { expect(getClosestTimestampInFullDataset(TEST_TIME_SERIES_DATA, 1690386199722.634)).toEqual(1690386195000); }); }); + +describe('batchDispatchNearbySeriesActions', () => { + function makeChartMock(): { chart: EChartsInstance; calls: Array<{ type: string; payload: unknown }> } { + const calls: Array<{ type: string; payload: unknown }> = []; + const chart = { + dispatchAction: (payload: { type: string; [k: string]: unknown }) => { + calls.push({ type: payload.type, payload: JSON.parse(JSON.stringify(payload)) }); + }, + } as unknown as EChartsInstance; + return { chart, calls }; + } + + it('dispatches a blanket downplay to clear ECharts axis-triggered emphasis before highlighting the winner', () => { + const { chart, calls } = makeChartMock(); + const winnerDatapoint: DatapointInfo = { seriesIndex: 3, dataIndex: 5, seriesName: 's3', yValue: 42 }; + + batchDispatchNearbySeriesActions(chart, [1, 2, 3, 4], [3], [1, 2, 4], [winnerDatapoint], []); + + const blanketDownplayIdx = calls.findIndex( + (c) => c.type === 'downplay' && (c.payload as { seriesIndex?: unknown }).seriesIndex === undefined + ); + const targetedDownplayIdx = calls.findIndex( + (c) => c.type === 'downplay' && Array.isArray((c.payload as { seriesIndex?: unknown }).seriesIndex) + ); + const highlightIdx = calls.findIndex((c) => c.type === 'highlight'); + + expect(blanketDownplayIdx).toBeGreaterThanOrEqual(0); + expect(targetedDownplayIdx).toBeGreaterThan(blanketDownplayIdx); + expect(highlightIdx).toBeGreaterThan(targetedDownplayIdx); + expect((calls[highlightIdx]!.payload as { seriesIndex: number[] }).seriesIndex).toEqual([3]); + }); + + it('dispatches a select action on the winning datapoint', () => { + const { chart, calls } = makeChartMock(); + const winnerDatapoint: DatapointInfo = { seriesIndex: 7, dataIndex: 11, seriesName: 's7', yValue: 1.5 }; + + batchDispatchNearbySeriesActions(chart, [7], [7], [], [winnerDatapoint], []); + + const selectCall = calls.find((c) => c.type === 'select'); + expect(selectCall).toBeDefined(); + expect((selectCall!.payload as { seriesIndex: number; dataIndex: number }).seriesIndex).toBe(7); + expect((selectCall!.payload as { seriesIndex: number; dataIndex: number }).dataIndex).toBe(11); + }); + + it('uses the last duplicate datapoint for select when duplicates exist (avoids color mismatch)', () => { + const { chart, calls } = makeChartMock(); + const winner: DatapointInfo = { seriesIndex: 1, dataIndex: 0, seriesName: 's1', yValue: 100 }; + const duplicate: DatapointInfo = { seriesIndex: 2, dataIndex: 0, seriesName: 's2', yValue: 100 }; + + batchDispatchNearbySeriesActions(chart, [1, 2], [1, 2], [], [winner, duplicate], [duplicate]); + + const selectCall = calls.find((c) => c.type === 'select'); + expect((selectCall!.payload as { seriesIndex: number }).seriesIndex).toBe(2); + }); + + it('falls back to highlighting all nearby series when no emphasized winner exists', () => { + const { chart, calls } = makeChartMock(); + + batchDispatchNearbySeriesActions(chart, [1, 2, 3], [], [1, 2, 3], [], []); + + const highlight = calls.find((c) => c.type === 'highlight'); + expect(highlight).toBeDefined(); + expect((highlight!.payload as { seriesIndex: number[]; notBlur: boolean }).seriesIndex).toEqual([1, 2, 3]); + expect((highlight!.payload as { seriesIndex: number[]; notBlur: boolean }).notBlur).toBe(true); + expect(calls.some((c) => c.type === 'toggleSelect')).toBe(true); + }); +}); diff --git a/components/src/utils/chart-actions.ts b/components/src/utils/chart-actions.ts index 50330c0c..a05cd565 100644 --- a/components/src/utils/chart-actions.ts +++ b/components/src/utils/chart-actions.ts @@ -117,13 +117,22 @@ export function batchDispatchNearbySeriesActions( }); } - // Clears emphasis state of all lines that are not emphasized. - // Emphasized is a subset of just the nearby series that are closest to cursor. + // Blanket downplay clears axis-triggered emphasis (enlarged "big point" markers) before + // re-applying emphasis to only the winner series. + // https://echarts.apache.org/en/api.html#action.downplay chart.dispatchAction({ type: 'downplay', - seriesIndex: nonEmphasizedSeriesIndexes, }); + // Clears emphasis state of all lines that are not emphasized. + // Emphasized is a subset of just the nearby series that are closest to cursor. + if (nonEmphasizedSeriesIndexes.length > 0) { + chart.dispatchAction({ + type: 'downplay', + seriesIndex: nonEmphasizedSeriesIndexes, + }); + } + // https://echarts.apache.org/en/api.html#action.highlight if (emphasizedSeriesIndexes.length > 0) { // Fadeout opacity of all series not closest to cursor. From 180780ed009ee7442f85af44e9916243d124d975 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 9 Jul 2026 12:57:59 +0200 Subject: [PATCH 07/17] [ENHANCEMENT] EChart: extract timezone-aware axis label formatter (#190) * [ENHANCEMENT] EChart: extract timezone-aware axis label formatter This util is used by the timeseries, scatter, heaptmap and statushistory charts. Signed-off-by: Andreas Gerstmayr * update constants Signed-off-by: Andreas Gerstmayr --------- Signed-off-by: Andreas Gerstmayr --- components/src/EChart/index.ts | 1 + .../src/EChart/timezone-formatter.test.ts | 79 +++++++++++++++++++ components/src/EChart/timezone-formatter.ts | 50 ++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 components/src/EChart/timezone-formatter.test.ts create mode 100644 components/src/EChart/timezone-formatter.ts diff --git a/components/src/EChart/index.ts b/components/src/EChart/index.ts index 466ab36b..a40ab1cd 100644 --- a/components/src/EChart/index.ts +++ b/components/src/EChart/index.ts @@ -12,3 +12,4 @@ // limitations under the License. export * from './EChart'; +export * from './timezone-formatter'; diff --git a/components/src/EChart/timezone-formatter.test.ts b/components/src/EChart/timezone-formatter.test.ts new file mode 100644 index 00000000..49d36d86 --- /dev/null +++ b/components/src/EChart/timezone-formatter.test.ts @@ -0,0 +1,79 @@ +// Copyright 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 { createTimezoneAwareAxisFormatter } from './timezone-formatter'; + +// Mock formatWithTimeZone since it's from @perses-dev/components +jest.mock('@perses-dev/components', () => ({ + formatWithTimeZone: jest.fn((date: Date, format: string, timeZone: string) => { + // Simple mock that returns format pattern with timezone + return `${format}[${timeZone}]`; + }), +})); + +describe('createTimezoneAwareAxisFormatter', () => { + const testTimestamp = 1640995200000; // 2022-01-01 00:00:00 UTC + const timeZone = 'America/New_York'; + + it('should format for ranges > 5 years with year format', () => { + const formatter = createTimezoneAwareAxisFormatter(6 * 365 * 24 * 60 * 60 * 1000, timeZone); + const result = formatter(testTimestamp); + expect(result).toBe('yyyy[America/New_York]'); + }); + + it('should format for ranges > 6 months with month-year format', () => { + const formatter = createTimezoneAwareAxisFormatter(3 * 365 * 24 * 60 * 60 * 1000, timeZone); + const result = formatter(testTimestamp); + expect(result).toBe('MMM yyyy[America/New_York]'); + }); + + it('should format for ranges between 10 days and 6 months with day-month format', () => { + const formatter = createTimezoneAwareAxisFormatter(30 * 24 * 60 * 60 * 1000, timeZone); // 30 days + const result = formatter(testTimestamp); + expect(result).toBe('dd.MM[America/New_York]'); + }); + + it('should format for ranges between 2-10 days with day-month-time format', () => { + const formatter = createTimezoneAwareAxisFormatter(5 * 24 * 60 * 60 * 1000, timeZone); // 5 days + const result = formatter(testTimestamp); + expect(result).toBe('dd.MM HH:mm[America/New_York]'); + }); + + it('should format for ranges <= 2 days with time format', () => { + const formatter = createTimezoneAwareAxisFormatter(6 * 60 * 60 * 1000, timeZone); // 6 hours + const result = formatter(testTimestamp); + expect(result).toBe('HH:mm[America/New_York]'); + }); + + it('should handle different timezones', () => { + const formatter = createTimezoneAwareAxisFormatter(6 * 60 * 60 * 1000, 'Europe/Prague'); + const result = formatter(testTimestamp); + expect(result).toBe('HH:mm[Europe/Prague]'); + }); + + it('should handle edge case at exactly 5 years', () => { + const fiveYears = 5 * 365 * 24 * 60 * 60 * 1000; + const formatter = createTimezoneAwareAxisFormatter(fiveYears, timeZone); + const result = formatter(testTimestamp); + // Should use MMM yyyy format (not > 5 years) + expect(result).toBe('MMM yyyy[America/New_York]'); + }); + + it('should handle edge case at exactly 2 days', () => { + const twoDays = 2 * 24 * 60 * 60 * 1000; + const formatter = createTimezoneAwareAxisFormatter(twoDays, timeZone); + const result = formatter(testTimestamp); + // Should use HH:mm format (not > 2 days) + expect(result).toBe('HH:mm[America/New_York]'); + }); +}); diff --git a/components/src/EChart/timezone-formatter.ts b/components/src/EChart/timezone-formatter.ts new file mode 100644 index 00000000..dfb8c038 --- /dev/null +++ b/components/src/EChart/timezone-formatter.ts @@ -0,0 +1,50 @@ +// Copyright 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 { formatWithTimeZone } from '@perses-dev/components'; + +const DAY_MS = 1000 * 60 * 60 * 24; +const MONTH_MS = DAY_MS * 30; +const YEAR_MS = DAY_MS * 365; + +/** + * Creates a timezone-aware axis formatter function for different time ranges + */ +export function createTimezoneAwareAxisFormatter(rangeMs: number, timeZone: string) { + return function (value: number): string { + const timeStamp = new Date(Number(value)); + + // more than 5 years + if (rangeMs > YEAR_MS * 5) { + return formatWithTimeZone(timeStamp, 'yyyy', timeZone); + } + + // more than 6 months + if (rangeMs > MONTH_MS * 6) { + return formatWithTimeZone(timeStamp, 'MMM yyyy', timeZone); + } + + // more than 10 days + if (rangeMs > DAY_MS * 10) { + return formatWithTimeZone(timeStamp, 'dd.MM', timeZone); + } + + // more than 2 days + if (rangeMs > DAY_MS * 2) { + return formatWithTimeZone(timeStamp, 'dd.MM HH:mm', timeZone); + } + + // less or equal 2 days + return formatWithTimeZone(timeStamp, 'HH:mm', timeZone); + }; +} From 5e546bac5e8b79060f731ec017a0af58db4a9e73 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 16 Jul 2026 10:26:01 +0200 Subject: [PATCH 08/17] [BUGFIX] fix race condition with initial query plugin spec (#199) useDefaultQueryDefinition() returned isLoading: false while the plugin was still loading (only the metadata loading state was checked). This caused the initial plugin spec to be set to {} because defaultQueryPlugin was undefined. Fix by combining both the metadata and plugin loading states into a single composite isLoading flag. Signed-off-by: Andreas Gerstmayr --- .../src/components/MultiQueryEditor/MultiQueryEditor.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx b/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx index 3375bdf5..3b2bafd3 100644 --- a/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx +++ b/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx @@ -54,7 +54,7 @@ function useDefaultQueryDefinition( defaultQueryKind = defaultPluginKinds?.[defaultQueryType] ?? queryPlugins?.[0]?.spec.name ?? ''; } - const { data: defaultQueryPlugin } = usePlugin(defaultQueryType, defaultQueryKind, { + const { data: defaultQueryPlugin, isLoading: isPluginLoading } = usePlugin(defaultQueryType, defaultQueryKind, { useErrorBoundary: true, enabled: true, }); @@ -67,7 +67,7 @@ function useDefaultQueryDefinition( plugin: { kind: defaultQueryKind, spec: defaultQueryPlugin?.createInitialOptions() || {} }, }, }, - isLoading, + isLoading: isLoading || isPluginLoading, }; } From a12a5db467d9c1c8350f33ff391940e1854b0ef2 Mon Sep 17 00:00:00 2001 From: Augustin Husson Date: Thu, 16 Jul 2026 10:26:11 +0200 Subject: [PATCH 09/17] =?UTF-8?q?[ignore]=C2=A0update=20perses/spec=20to?= =?UTF-8?q?=20beta.9=20(#200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Augustin Husson --- client/package.json | 2 +- components/package.json | 2 +- cue/cue.mod/module.cue | 2 +- dashboards/package.json | 2 +- package-lock.json | 14 +++++++------- plugin-system/package.json | 2 +- plugin-system/src/remote/PluginRuntime.tsx | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/client/package.json b/client/package.json index 10c0c8ca..fa9095be 100644 --- a/client/package.json +++ b/client/package.json @@ -15,7 +15,7 @@ "main": "dist/cjs/index.js", "types": "dist/index.d.ts", "dependencies": { - "@perses-dev/spec": "0.2.0-beta.6", + "@perses-dev/spec": "0.2.0-beta.9", "zod": "^3.21.4" }, "scripts": { diff --git a/components/package.json b/components/package.json index 53a08e39..12282252 100644 --- a/components/package.json +++ b/components/package.json @@ -34,7 +34,7 @@ "@date-fns/tz": "^1.4.1", "@fontsource/inter": "^5.0.0", "@mui/x-date-pickers": "^7.23.1", - "@perses-dev/spec": "0.2.0-beta.6", + "@perses-dev/spec": "0.2.0-beta.9", "@perses-dev/client": "0.54.0-beta.11", "numbro": "^2.3.6", "@tanstack/match-sorter-utils": "^8.19.4", diff --git a/cue/cue.mod/module.cue b/cue/cue.mod/module.cue index 1fc7fb85..1f5a0014 100644 --- a/cue/cue.mod/module.cue +++ b/cue/cue.mod/module.cue @@ -7,7 +7,7 @@ source: { } deps: { "github.com/perses/spec/cue@v0": { - v: "v0.2.0-beta.6" + v: "v0.2.0-beta.9" default: true } } diff --git a/dashboards/package.json b/dashboards/package.json index 30c92849..f1efa029 100644 --- a/dashboards/package.json +++ b/dashboards/package.json @@ -31,7 +31,7 @@ "dependencies": { "@perses-dev/components": "0.54.0-beta.11", "@perses-dev/plugin-system": "0.54.0-beta.11", - "@perses-dev/spec": "0.2.0-beta.6", + "@perses-dev/spec": "0.2.0-beta.9", "@perses-dev/client": "0.54.0-beta.11", "@tanstack/hotkeys": "^0.8.0", "@tanstack/react-hotkeys": "^0.9.1", diff --git a/package-lock.json b/package-lock.json index 925b64af..2e22d72f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,7 +58,7 @@ "version": "0.54.0-beta.11", "license": "Apache-2.0", "dependencies": { - "@perses-dev/spec": "0.2.0-beta.6", + "@perses-dev/spec": "0.2.0-beta.9", "zod": "^3.21.4" } }, @@ -74,7 +74,7 @@ "@fontsource/inter": "^5.0.0", "@mui/x-date-pickers": "^7.23.1", "@perses-dev/client": "0.54.0-beta.11", - "@perses-dev/spec": "0.2.0-beta.6", + "@perses-dev/spec": "0.2.0-beta.9", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-table": "^8.20.5", "@uiw/react-codemirror": "^4.19.1", @@ -110,7 +110,7 @@ "@perses-dev/client": "0.54.0-beta.11", "@perses-dev/components": "0.54.0-beta.11", "@perses-dev/plugin-system": "0.54.0-beta.11", - "@perses-dev/spec": "0.2.0-beta.6", + "@perses-dev/spec": "0.2.0-beta.9", "@tanstack/hotkeys": "^0.8.0", "@tanstack/react-hotkeys": "^0.9.1", "immer": "^10.1.1", @@ -3353,9 +3353,9 @@ "link": true }, "node_modules/@perses-dev/spec": { - "version": "0.2.0-beta.6", - "resolved": "https://registry.npmjs.org/@perses-dev/spec/-/spec-0.2.0-beta.6.tgz", - "integrity": "sha512-J8oWuZgc/0nlT8PDlEIahA7XZ/tKITC9C0FG7e+FFhYGRzZ5oyMz2keuPkAzl3KPYB9yeczit9TFQWsoY/ot5Q==", + "version": "0.2.0-beta.9", + "resolved": "https://registry.npmjs.org/@perses-dev/spec/-/spec-0.2.0-beta.9.tgz", + "integrity": "sha512-hB+xmz5nnFWOypYF8UBzqL8YuHaqKVHzRBVlFj6f88ZtLw3j8bxrnMkQ/bSGwgsrEKzbdKWrCrtraokzl6nccA==", "license": "Apache-2.0", "dependencies": { "date-fns": "^4.1.0", @@ -15705,7 +15705,7 @@ "@perses-dev/client": "0.54.0-beta.11", "@perses-dev/components": "0.54.0-beta.11", "@perses-dev/core": "0.53.0", - "@perses-dev/spec": "0.2.0-beta.6", + "@perses-dev/spec": "0.2.0-beta.9", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "immer": "^10.1.1", diff --git a/plugin-system/package.json b/plugin-system/package.json index 189a185d..ffb88f87 100644 --- a/plugin-system/package.json +++ b/plugin-system/package.json @@ -31,7 +31,7 @@ "@module-federation/enhanced": "^2.6.0", "@perses-dev/components": "0.54.0-beta.11", "@perses-dev/core": "0.53.0", - "@perses-dev/spec": "0.2.0-beta.6", + "@perses-dev/spec": "0.2.0-beta.9", "@perses-dev/client": "0.54.0-beta.11", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", diff --git a/plugin-system/src/remote/PluginRuntime.tsx b/plugin-system/src/remote/PluginRuntime.tsx index 900da546..7129f62c 100644 --- a/plugin-system/src/remote/PluginRuntime.tsx +++ b/plugin-system/src/remote/PluginRuntime.tsx @@ -91,11 +91,11 @@ const getPluginRuntime = (): ModuleFederation => { }, }, '@perses-dev/spec': { - version: '0.2.0-beta.6', + version: '0.2.0-beta.9', lib: () => require('@perses-dev/spec'), shareConfig: { singleton: true, - requiredVersion: '^0.2.0-beta.6', + requiredVersion: '^0.2.0-beta.9', }, }, '@perses-dev/core': { From 9bd2cb02c190fdb94d684eafdffc6bf2a8d733cf Mon Sep 17 00:00:00 2001 From: Augustin Husson Date: Thu, 16 Jul 2026 14:43:18 +0200 Subject: [PATCH 10/17] Release v0.54.0-rc.0 (#201) Signed-off-by: Augustin Husson --- client/package.json | 2 +- components/package.json | 4 +-- dashboards/package.json | 8 +++--- explore/package.json | 8 +++--- package-lock.json | 32 +++++++++++----------- package.json | 2 +- plugin-system/package.json | 6 ++-- plugin-system/src/remote/PluginRuntime.tsx | 20 +++++++------- 8 files changed, 41 insertions(+), 41 deletions(-) diff --git a/client/package.json b/client/package.json index fa9095be..4211d0cc 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/client", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "description": "Functions as an API client or Data fetching Layer for interacting with a backend service", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", diff --git a/components/package.json b/components/package.json index 12282252..502a764d 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/components", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "description": "Common UI components used across Perses features", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -35,7 +35,7 @@ "@fontsource/inter": "^5.0.0", "@mui/x-date-pickers": "^7.23.1", "@perses-dev/spec": "0.2.0-beta.9", - "@perses-dev/client": "0.54.0-beta.11", + "@perses-dev/client": "0.54.0-rc.0", "numbro": "^2.3.6", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-table": "^8.20.5", diff --git a/dashboards/package.json b/dashboards/package.json index f1efa029..cd670181 100644 --- a/dashboards/package.json +++ b/dashboards/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/dashboards", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "description": "The dashboards feature in Perses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -29,10 +29,10 @@ "lint:fix": "eslint --fix src --ext .ts,.tsx" }, "dependencies": { - "@perses-dev/components": "0.54.0-beta.11", - "@perses-dev/plugin-system": "0.54.0-beta.11", + "@perses-dev/components": "0.54.0-rc.0", + "@perses-dev/plugin-system": "0.54.0-rc.0", "@perses-dev/spec": "0.2.0-beta.9", - "@perses-dev/client": "0.54.0-beta.11", + "@perses-dev/client": "0.54.0-rc.0", "@tanstack/hotkeys": "^0.8.0", "@tanstack/react-hotkeys": "^0.9.1", "immer": "^10.1.1", diff --git a/explore/package.json b/explore/package.json index f419a229..c3ca19ae 100644 --- a/explore/package.json +++ b/explore/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/explore", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "description": "The explore feature in Perses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -28,9 +28,9 @@ }, "dependencies": { "@nexucis/fuzzy": "^0.5.1", - "@perses-dev/components": "0.54.0-beta.11", - "@perses-dev/dashboards": "0.54.0-beta.11", - "@perses-dev/plugin-system": "0.54.0-beta.11", + "@perses-dev/components": "0.54.0-rc.0", + "@perses-dev/dashboards": "0.54.0-rc.0", + "@perses-dev/plugin-system": "0.54.0-rc.0", "mdi-material-ui": "^7.9.2", "qs": "^6.14.0", "react-virtuoso": "^4.12.2", diff --git a/package-lock.json b/package-lock.json index 2e22d72f..02ffd4e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "perses-shared", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "perses-shared", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "workspaces": [ "components", "dashboards", @@ -55,7 +55,7 @@ }, "client": { "name": "@perses-dev/client", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "license": "Apache-2.0", "dependencies": { "@perses-dev/spec": "0.2.0-beta.9", @@ -64,7 +64,7 @@ }, "components": { "name": "@perses-dev/components", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "license": "Apache-2.0", "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.4.0", @@ -73,7 +73,7 @@ "@date-fns/tz": "^1.4.1", "@fontsource/inter": "^5.0.0", "@mui/x-date-pickers": "^7.23.1", - "@perses-dev/client": "0.54.0-beta.11", + "@perses-dev/client": "0.54.0-rc.0", "@perses-dev/spec": "0.2.0-beta.9", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-table": "^8.20.5", @@ -104,12 +104,12 @@ }, "dashboards": { "name": "@perses-dev/dashboards", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "license": "Apache-2.0", "dependencies": { - "@perses-dev/client": "0.54.0-beta.11", - "@perses-dev/components": "0.54.0-beta.11", - "@perses-dev/plugin-system": "0.54.0-beta.11", + "@perses-dev/client": "0.54.0-rc.0", + "@perses-dev/components": "0.54.0-rc.0", + "@perses-dev/plugin-system": "0.54.0-rc.0", "@perses-dev/spec": "0.2.0-beta.9", "@tanstack/hotkeys": "^0.8.0", "@tanstack/react-hotkeys": "^0.9.1", @@ -176,13 +176,13 @@ }, "explore": { "name": "@perses-dev/explore", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "license": "Apache-2.0", "dependencies": { "@nexucis/fuzzy": "^0.5.1", - "@perses-dev/components": "0.54.0-beta.11", - "@perses-dev/dashboards": "0.54.0-beta.11", - "@perses-dev/plugin-system": "0.54.0-beta.11", + "@perses-dev/components": "0.54.0-rc.0", + "@perses-dev/dashboards": "0.54.0-rc.0", + "@perses-dev/plugin-system": "0.54.0-rc.0", "mdi-material-ui": "^7.9.2", "qs": "^6.14.0", "react-virtuoso": "^4.12.2", @@ -15698,12 +15698,12 @@ }, "plugin-system": { "name": "@perses-dev/plugin-system", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "license": "Apache-2.0", "dependencies": { "@module-federation/enhanced": "^2.6.0", - "@perses-dev/client": "0.54.0-beta.11", - "@perses-dev/components": "0.54.0-beta.11", + "@perses-dev/client": "0.54.0-rc.0", + "@perses-dev/components": "0.54.0-rc.0", "@perses-dev/core": "0.53.0", "@perses-dev/spec": "0.2.0-beta.9", "date-fns": "^4.1.0", diff --git a/package.json b/package.json index 6b20520e..74f09add 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "perses-shared", "description": "Monorepo for the Perses UI shared packages", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "private": true, "scripts": { "build": "turbo run build", diff --git a/plugin-system/package.json b/plugin-system/package.json index ffb88f87..c26e4620 100644 --- a/plugin-system/package.json +++ b/plugin-system/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/plugin-system", - "version": "0.54.0-beta.11", + "version": "0.54.0-rc.0", "description": "The plugin feature in Pereses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -29,10 +29,10 @@ }, "dependencies": { "@module-federation/enhanced": "^2.6.0", - "@perses-dev/components": "0.54.0-beta.11", + "@perses-dev/components": "0.54.0-rc.0", "@perses-dev/core": "0.53.0", "@perses-dev/spec": "0.2.0-beta.9", - "@perses-dev/client": "0.54.0-beta.11", + "@perses-dev/client": "0.54.0-rc.0", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "immer": "^10.1.1", diff --git a/plugin-system/src/remote/PluginRuntime.tsx b/plugin-system/src/remote/PluginRuntime.tsx index 7129f62c..885d404f 100644 --- a/plugin-system/src/remote/PluginRuntime.tsx +++ b/plugin-system/src/remote/PluginRuntime.tsx @@ -107,43 +107,43 @@ const getPluginRuntime = (): ModuleFederation => { }, }, '@perses-dev/client': { - version: '0.54.0-beta.11', + version: '0.54.0-rc.0', lib: () => require('@perses-dev/client'), shareConfig: { singleton: true, - requiredVersion: '^0.54.0-beta.11', + requiredVersion: '^0.54.0-rc.0', }, }, '@perses-dev/components': { - version: '0.54.0-beta.11', + version: '0.54.0-rc.0', lib: () => require('@perses-dev/components'), shareConfig: { singleton: true, - requiredVersion: '^0.54.0-beta.11', + requiredVersion: '^0.54.0-rc.0', }, }, '@perses-dev/plugin-system': { - version: '0.54.0-beta.11', + version: '0.54.0-rc.0', lib: () => require('@perses-dev/plugin-system'), shareConfig: { singleton: true, - requiredVersion: '^0.54.0-beta.11', + requiredVersion: '^0.54.0-rc.0', }, }, '@perses-dev/explore': { - version: '0.54.0-beta.11', + version: '0.54.0-rc.0', lib: () => require('@perses-dev/explore'), shareConfig: { singleton: true, - requiredVersion: '^0.54.0-beta.11', + requiredVersion: '^0.54.0-rc.0', }, }, '@perses-dev/dashboards': { - version: '0.54.0-beta.11', + version: '0.54.0-rc.0', lib: () => require('@perses-dev/dashboards'), shareConfig: { singleton: true, - requiredVersion: '^0.54.0-beta.11', + requiredVersion: '^0.54.0-rc.0', }, }, // Below are the shared modules that are used by the plugins, this can be part of the SDK From 472a289d1d59e728894395ed8c5689bda201c48d Mon Sep 17 00:00:00 2001 From: Gabriel Bernal Date: Mon, 20 Jul 2026 11:43:35 +0200 Subject: [PATCH 11/17] [IGNORE] fix vulnerable dependencies (#203) Signed-off-by: Gabriel Bernal --- go.mod | 4 +- go.sum | 4 +- package-lock.json | 594 +++++++++++-------------------------- plugin-system/package.json | 13 +- 4 files changed, 193 insertions(+), 422 deletions(-) diff --git a/go.mod b/go.mod index c5bd34f0..51ec5ce6 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/perses/shared -go 1.26.0 +go 1.26.5 require ( github.com/perses/perses v0.53.1 @@ -9,5 +9,5 @@ require ( require ( github.com/perses/common v0.30.2 // indirect - golang.org/x/sys v0.41.0 // indirect + golang.org/x/sys v0.44.0 // indirect ) diff --git a/go.sum b/go.sum index 9b61ff55..35221a79 100644 --- a/go.sum +++ b/go.sum @@ -10,7 +10,7 @@ github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/package-lock.json b/package-lock.json index 02ffd4e8..d6f65cc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1474,9 +1474,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", "dev": true, "license": "MIT", "dependencies": { @@ -2079,36 +2079,22 @@ "license": "MIT" }, "node_modules/@module-federation/bridge-react-webpack-plugin": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-2.6.0.tgz", - "integrity": "sha512-V+4+1PUmDgAozXPJA8evIa2G+dPJGYn1JzDoHS9wMFch2blko/DO2sMq6FGuZFnT1oyjxiWeiaWXluRElOU9rQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-2.8.0.tgz", + "integrity": "sha512-7AaaiE4YOXFb+st6xlVDK65aNKZYR9S9ykH0q2mkHAHTgquXciAjypS8JuhmKn7Lob/2rkplMOc4YsGxG3Ng9Q==", "license": "MIT", "dependencies": { - "@module-federation/sdk": "2.6.0", - "@types/semver": "7.5.8", - "semver": "7.6.3" - } - }, - "node_modules/@module-federation/bridge-react-webpack-plugin/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "@module-federation/sdk": "2.8.0" } }, "node_modules/@module-federation/cli": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/cli/-/cli-2.6.0.tgz", - "integrity": "sha512-CHBsi5f8Oe9sCN6rY4R0+P/oFApvuutnqfoYNtuORdMn6FHittFDkuLFCSYlrZN2QYw/Sqir2xgLcwmY77Z7PA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/cli/-/cli-2.8.0.tgz", + "integrity": "sha512-yTxdWkCJPPo+IGASz+NqdW13cw3DhjSBEn9r85aYn1ahckDwB1WcZe0OjDWMqCcR6yi0BMLedk0SWrUeUj0fWw==", "license": "MIT", "dependencies": { - "@module-federation/dts-plugin": "2.6.0", - "@module-federation/sdk": "2.6.0", + "@module-federation/dts-plugin": "2.8.0", + "@module-federation/sdk": "2.8.0", "commander": "11.1.0", "jiti": "2.4.2" }, @@ -2129,24 +2115,22 @@ } }, "node_modules/@module-federation/dts-plugin": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-2.6.0.tgz", - "integrity": "sha512-0dMjLhU+2HNPyX5Txu6JqA2CAYxVLRv3c/bpRk58aNVwhDO/jqp66IdFztdMZ1XiHqOW9jB3pkOSuIpcl/yXpQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-2.8.0.tgz", + "integrity": "sha512-defjq4jOWMEfeejezPWLP5sc8kw0O6FqTT7/E5rbZPEVyjB1A0U3ynhW6GDE5/6hk9/TzdbWS+fBNi4MqUOY6Q==", "license": "MIT", "dependencies": { - "@module-federation/error-codes": "2.6.0", - "@module-federation/managers": "2.6.0", - "@module-federation/sdk": "2.6.0", - "@module-federation/third-party-dts-extractor": "2.6.0", + "@module-federation/error-codes": "2.8.0", + "@module-federation/managers": "2.8.0", + "@module-federation/sdk": "2.8.0", + "@module-federation/third-party-dts-extractor": "2.8.0", "adm-zip": "0.5.10", - "ansi-colors": "4.1.3", "isomorphic-ws": "5.0.0", - "node-schedule": "2.1.1", "undici": "7.28.0", "ws": "8.21.0" }, "peerDependencies": { - "typescript": "^4.9.0 || ^5.0.0", + "typescript": "^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "vue-tsc": ">=1.0.24" }, "peerDependenciesMeta": { @@ -2156,31 +2140,30 @@ } }, "node_modules/@module-federation/enhanced": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-2.6.0.tgz", - "integrity": "sha512-pQ0V93FfeHtr67Nusr6ySTQO1qeOGs1x9MMjbeZ4ObOPbXdHNX1tH19xVP8mo5k3e8iIV2teMoSayTmHT+nD0g==", - "license": "MIT", - "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "2.6.0", - "@module-federation/cli": "2.6.0", - "@module-federation/dts-plugin": "2.6.0", - "@module-federation/error-codes": "2.6.0", - "@module-federation/inject-external-runtime-core-plugin": "2.6.0", - "@module-federation/managers": "2.6.0", - "@module-federation/manifest": "2.6.0", - "@module-federation/rspack": "2.6.0", - "@module-federation/runtime-tools": "2.6.0", - "@module-federation/sdk": "2.6.0", - "@module-federation/webpack-bundler-runtime": "2.6.0", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-2.8.0.tgz", + "integrity": "sha512-h8vkLdhK7tlcSPmyYNGfGyt0pSzfDB0tYVYdyUt2tXwQRfaJAi3bsIpujMXElw3MXtOfSHESa6M/hPKrtWTHBw==", + "license": "MIT", + "dependencies": { + "@module-federation/bridge-react-webpack-plugin": "2.8.0", + "@module-federation/cli": "2.8.0", + "@module-federation/dts-plugin": "2.8.0", + "@module-federation/error-codes": "2.8.0", + "@module-federation/inject-external-runtime-core-plugin": "2.8.0", + "@module-federation/managers": "2.8.0", + "@module-federation/manifest": "2.8.0", + "@module-federation/rspack": "2.8.0", + "@module-federation/runtime-tools": "2.8.0", + "@module-federation/sdk": "2.8.0", + "@module-federation/webpack-bundler-runtime": "2.8.0", "schema-utils": "4.3.0", - "tapable": "2.3.0", - "upath": "2.0.1" + "tapable": "2.3.0" }, "bin": { "mf": "bin/mf.js" }, "peerDependencies": { - "typescript": "^4.9.0 || ^5.0.0", + "typescript": "^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "vue-tsc": ">=1.0.24", "webpack": "^5.0.0" }, @@ -2263,59 +2246,57 @@ } }, "node_modules/@module-federation/error-codes": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-2.6.0.tgz", - "integrity": "sha512-J9opjWJJZ1JI/9EvNZF8Ps1VMHS9EsH/i0UjCkQQxFVYZxSDBX1CFunvc7awac4cY//LqJUfqBbfoQPhCfKKKw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-2.8.0.tgz", + "integrity": "sha512-Gaog9904EmxYOQV0hli3XQ7jXeFaADfh5bnBtTCtbZ37Qd/Sz9kQfd+gYQRyIj7RGmkv9DPiN/SsmrTMrTymKw==", "license": "MIT" }, "node_modules/@module-federation/inject-external-runtime-core-plugin": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-2.6.0.tgz", - "integrity": "sha512-x5SmH33U1nSEcBXG6YzvkwIQLoKcd/CNfrAx4IJ4qBzlQKI0NvY7U4JE83LpBnwahNTQLvp27ZVX+1f7kt4Vww==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-2.8.0.tgz", + "integrity": "sha512-fW3jD1ZVds6r/Ul8TtUA42RsB0LfT1yjo5KjqgirH9QrmEMH21x44e3e6BC6IN820XKawRDSmz2kFA5YHIQp7Q==", "license": "MIT", "peerDependencies": { - "@module-federation/runtime-tools": "2.6.0" + "@module-federation/runtime-tools": "2.8.0" } }, "node_modules/@module-federation/managers": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-2.6.0.tgz", - "integrity": "sha512-7Y4Ri6Lh10dCHoEJvNGFmK2Gg1JKy7oJ1TEZhuU3n3mgIpZ519fDNRvU4+tiO9C49p1xyK+mQ8ewxth83waKUA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-2.8.0.tgz", + "integrity": "sha512-SnVBCwmi962WGg6hLFElxZUCnrRJdR6glE2ZKPBY/iK07AHUN2ZxuaBCBsVzyws+xLZGHZxBHmVstijTh8dSUA==", "license": "MIT", "dependencies": { - "@module-federation/sdk": "2.6.0", - "find-pkg": "2.0.0" + "@module-federation/sdk": "2.8.0" } }, "node_modules/@module-federation/manifest": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-2.6.0.tgz", - "integrity": "sha512-wIjI4wgFKBoq4l/iBOT2lva+i5sPv1+Ci1gOLOfjMAkygyGo97csUG5TaCWgLJpZzbYrpbFObCB2wZhqaLrQIw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-2.8.0.tgz", + "integrity": "sha512-wfVeBXc4/C2F70nRFSPqJhkcwbDgo+wQyEn3jbjJTDoUqxxhYBfHFs1ACBYOk3Qm97L7hHclHGtUG0/nvDEfAA==", "license": "MIT", "dependencies": { - "@module-federation/dts-plugin": "2.6.0", - "@module-federation/managers": "2.6.0", - "@module-federation/sdk": "2.6.0", - "find-pkg": "2.0.0" + "@module-federation/dts-plugin": "2.8.0", + "@module-federation/managers": "2.8.0", + "@module-federation/sdk": "2.8.0" } }, "node_modules/@module-federation/rspack": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-2.6.0.tgz", - "integrity": "sha512-Ayh7WLxhLRMyfBfajDwEytR5StFfnqf8p4tPHq5ds+HzJMlGz/M+NIYEzdOpfOFdE5IqN9bY/mj2AFI5lCureQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-2.8.0.tgz", + "integrity": "sha512-TPcrkHpaZgL25Vx3c8oSNwyv7/KktC7uo6HTQdVWlFzbq5RSoMMGkWoir5pY5124isae2/p6v5xAuqICi4r0Zg==", "license": "MIT", "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "2.6.0", - "@module-federation/dts-plugin": "2.6.0", - "@module-federation/inject-external-runtime-core-plugin": "2.6.0", - "@module-federation/managers": "2.6.0", - "@module-federation/manifest": "2.6.0", - "@module-federation/runtime-tools": "2.6.0", - "@module-federation/sdk": "2.6.0" + "@module-federation/bridge-react-webpack-plugin": "2.8.0", + "@module-federation/dts-plugin": "2.8.0", + "@module-federation/inject-external-runtime-core-plugin": "2.8.0", + "@module-federation/managers": "2.8.0", + "@module-federation/manifest": "2.8.0", + "@module-federation/runtime-tools": "2.8.0", + "@module-federation/sdk": "2.8.0" }, "peerDependencies": { "@rspack/core": "^0.7.0 || ^1.0.0 || ^2.0.0-0", - "typescript": "^4.9.0 || ^5.0.0", + "typescript": "^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "vue-tsc": ">=1.0.24" }, "peerDependenciesMeta": { @@ -2328,86 +2309,57 @@ } }, "node_modules/@module-federation/runtime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-2.6.0.tgz", - "integrity": "sha512-Y8KgL70RDxD8cCtGWGlGkt+TSDleIjdGmS27gnllibQAIgG/vHhPD11r8kd92x44k4dqtMIntzreOphGICl92g==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-2.8.0.tgz", + "integrity": "sha512-cGtUBQ1/TVy7KrXy6xPgy3FEmOGyIYkBA2T4iGH3ZH5PNPPTmqN9jF2AfneTSOj0RtBr7Pxq3CUt81E/UCvK1A==", "license": "MIT", "dependencies": { - "@module-federation/error-codes": "2.6.0", - "@module-federation/runtime-core": "2.6.0", - "@module-federation/sdk": "2.6.0" + "@module-federation/error-codes": "2.8.0", + "@module-federation/runtime-core": "2.8.0", + "@module-federation/sdk": "2.8.0" } }, "node_modules/@module-federation/runtime-core": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-2.6.0.tgz", - "integrity": "sha512-9sL7Yj3H3COZI9JpK/J4hmJUBkIJdmowkj6phHuFiy5Hm5bHiE5U+9WBbR9KqTdyNhAVSL9FeprBW5/Io6i59A==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-2.8.0.tgz", + "integrity": "sha512-Tf98+epGGiPSHqmQHuXa2uXZMMvjGf1IqJDR1/FpXfmobv5ECN0mGZCjUHGNSyxvoDyXKIkKwJu7IwEoh0ouQA==", "license": "MIT", "dependencies": { - "@module-federation/error-codes": "2.6.0", - "@module-federation/sdk": "2.6.0" + "@module-federation/error-codes": "2.8.0", + "@module-federation/sdk": "2.8.0" } }, "node_modules/@module-federation/runtime-tools": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-2.6.0.tgz", - "integrity": "sha512-LfvihAhAWWNWlAL9zM+UDVDSSuLE2ZU0ATJ6dcbJuLstYbHYfUqq2e6IyU8TKLPXIib0VAWqPrT44TPam1VJ7g==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-2.8.0.tgz", + "integrity": "sha512-3yOqjdSHXxX4HA3GhlXg3hghGAXW2RJUsnwXCcik2/lTxOHizKI8f3RM+GGCKPxDVqtw43IShe3tA12jNL5A/A==", "license": "MIT", "dependencies": { - "@module-federation/runtime": "2.6.0", - "@module-federation/webpack-bundler-runtime": "2.6.0" + "@module-federation/runtime": "2.8.0", + "@module-federation/webpack-bundler-runtime": "2.8.0" } }, "node_modules/@module-federation/sdk": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-2.6.0.tgz", - "integrity": "sha512-Z3HT1uDciMrvz2at3sw6TJbAK9Fl7RmoC/8iqO35HDtQMQJ1qSsMIAjnsiCpAC0D4nAm6PIqgSqPWRO/WYgo0Q==", - "license": "MIT", - "peerDependencies": { - "node-fetch": "^2.7.0 || ^3.3.2" - }, - "peerDependenciesMeta": { - "node-fetch": { - "optional": true - } - } + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-2.8.0.tgz", + "integrity": "sha512-yBP+9+0Z8nlvKEXAZS3AsQVy7bFbZf8eMivGk4q4ZdwG3TsLMlsPjb1dQb2i7gcAG6ux9y2LWLkj/0LVk74cnQ==", + "license": "MIT" }, "node_modules/@module-federation/third-party-dts-extractor": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-2.6.0.tgz", - "integrity": "sha512-rEC1YRC3gTafoOcFm1Htz6cAwHRoWEMQNCm1LXR1HiuayJzUkViVpK/1Pp37UZfytOyQ0qIaP6Ag81PdGvDbmw==", - "license": "MIT", - "dependencies": { - "find-pkg": "2.0.0", - "resolve": "1.22.8" - } - }, - "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", - "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" - } + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-2.8.0.tgz", + "integrity": "sha512-nAMlr74OKIylkfRwlunOhytQbmsgb3gCqdXWnPQhG+ZtqWXGELLfMT4a1Q1ht3cS+sRpWj2SZRqK2M7GadI6tA==", + "license": "MIT" }, "node_modules/@module-federation/webpack-bundler-runtime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-2.6.0.tgz", - "integrity": "sha512-JjuWOU3ktwDjBWhM4WCoeiErcUxcB5YwUnVjyTMfNJHC3+DMhG4m6ziCl5EJrCv+KaEQdcvXmxLliNZidBZGQg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-2.8.0.tgz", + "integrity": "sha512-82fDy9v+7qV5fiN8TKVhOdrxhmAZnUIX/IKivYX5ulCt8aoOzVFTiwm/P1GQUDD8z6dqR48xgJdZdf0548Mc9w==", "license": "MIT", "dependencies": { - "@module-federation/error-codes": "2.6.0", - "@module-federation/runtime": "2.6.0", - "@module-federation/sdk": "2.6.0" + "@module-federation/error-codes": "2.8.0", + "@module-federation/runtime": "2.8.0", + "@module-federation/sdk": "2.8.0" } }, "node_modules/@mui/core-downloads-tracker": { @@ -3258,13 +3210,13 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", - "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.2" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -3454,30 +3406,30 @@ } }, "node_modules/@rspack/binding": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-2.1.0.tgz", - "integrity": "sha512-LsXFIOOYDutHk44SAOcVQa5iA7lhYwEbD+nZhgmCiGJvKKh0UIpBj6EAsBsB6omEK5GEXvjDeLFieKgbYW08QQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-2.1.4.tgz", + "integrity": "sha512-iye4BaTYtTt0qa39avWwEsUobVxFNhQHr6B8reFeKU7sdaZsC9LUoDR8JAt5gOnbnzFMPdKgrdU/VzkC6rXbIg==", "license": "MIT", "peer": true, "optionalDependencies": { - "@rspack/binding-darwin-arm64": "2.1.0", - "@rspack/binding-darwin-x64": "2.1.0", - "@rspack/binding-linux-arm64-gnu": "2.1.0", - "@rspack/binding-linux-arm64-musl": "2.1.0", - "@rspack/binding-linux-riscv64-gnu": "2.1.0", - "@rspack/binding-linux-riscv64-musl": "2.1.0", - "@rspack/binding-linux-x64-gnu": "2.1.0", - "@rspack/binding-linux-x64-musl": "2.1.0", - "@rspack/binding-wasm32-wasi": "2.1.0", - "@rspack/binding-win32-arm64-msvc": "2.1.0", - "@rspack/binding-win32-ia32-msvc": "2.1.0", - "@rspack/binding-win32-x64-msvc": "2.1.0" + "@rspack/binding-darwin-arm64": "2.1.4", + "@rspack/binding-darwin-x64": "2.1.4", + "@rspack/binding-linux-arm64-gnu": "2.1.4", + "@rspack/binding-linux-arm64-musl": "2.1.4", + "@rspack/binding-linux-riscv64-gnu": "2.1.4", + "@rspack/binding-linux-riscv64-musl": "2.1.4", + "@rspack/binding-linux-x64-gnu": "2.1.4", + "@rspack/binding-linux-x64-musl": "2.1.4", + "@rspack/binding-wasm32-wasi": "2.1.4", + "@rspack/binding-win32-arm64-msvc": "2.1.4", + "@rspack/binding-win32-ia32-msvc": "2.1.4", + "@rspack/binding-win32-x64-msvc": "2.1.4" } }, "node_modules/@rspack/binding-darwin-arm64": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-2.1.0.tgz", - "integrity": "sha512-1DdnXLCl4/7BydtxvFyJbqOyvo3dgeKIdukr5BrM7UUA5rJnpin0qZIq/C0Y+ZwTx7ML4zdYaJeR+WOujRQH1Q==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-2.1.4.tgz", + "integrity": "sha512-3Xcs01iw48F4WeE4SHga6bCNb/UEFvtQX4P4eMIaJfGPjTQuxfabGE8yCPm9e3tpLZ5uo+IBnJ6nh5r6tIDOXQ==", "cpu": [ "arm64" ], @@ -3489,9 +3441,9 @@ "peer": true }, "node_modules/@rspack/binding-darwin-x64": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-2.1.0.tgz", - "integrity": "sha512-PJB6n/BaupvfLaErsfvC7q9W07WozkPe2Xw7sQqX6fblK+4tooBp0ZdAtKi76L+U2fR8t8/nQb0Jokco0co7Fg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-2.1.4.tgz", + "integrity": "sha512-bz/AsCplLs+3fXULPQU9d4r8H4PdeljgHFyItvVIrA/NKZqzQ8sX0topf/zJVZAOtPH7GNnrKjq0/F0U2DHikQ==", "cpu": [ "x64" ], @@ -3503,9 +3455,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-arm64-gnu": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-2.1.0.tgz", - "integrity": "sha512-TCmWIeI03ZZi8GjpIS2yl9JpaazsaA4F84zbX6a4kdZnFkrmFKRdvczZrquTNQvmggAEaJiPxkSrS8OC1LSAwA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-2.1.4.tgz", + "integrity": "sha512-x0HQTLU1MusCtNamuXxf3ayEPkvh9uuaq4wVyBqveRkn4FznSOoHUsxTAKMnjGARX+vdLV/y/SwWJRDp2RI4zw==", "cpu": [ "arm64" ], @@ -3517,9 +3469,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-arm64-musl": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-2.1.0.tgz", - "integrity": "sha512-HJzw5gG62qjj9fRQgj948naLucwE1Vg1bfcYHAxOr1/bVVIm4I4QvWGuqvd3XOu0MfLXPWvEyMAvJL+rtgamsw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-2.1.4.tgz", + "integrity": "sha512-SEYCQD9UflKJMkYGnG5nt2gcsqdkgJsQmryBC/jxo+bOuY9gUSReU99FqCt7WRQrsHLbGeAFeTWs1QzItWG/Bw==", "cpu": [ "arm64" ], @@ -3531,9 +3483,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-riscv64-gnu": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-2.1.0.tgz", - "integrity": "sha512-B3ENZHIBi5u1Apt6RJ62QSCabCijI5l86Sm2AEDYpQnqqBj3vIc+Br9HJHvNjK8PNWs1WfmD//UTUmQqZbYpKQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-2.1.4.tgz", + "integrity": "sha512-jYtQKtnDRaVfyasvTGY04Z7m+xDWZYVwAIEOB4hP7czM7FVLOMgHlMlvw/EgF0DNHrBthqbPfBIS2tP50CzpEA==", "cpu": [ "riscv64" ], @@ -3545,9 +3497,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-riscv64-musl": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-2.1.0.tgz", - "integrity": "sha512-Qho1S8bW2BKRsJjl/f39GoyPRznF8ZarIgxZdVCIkn4k+3veggKWxqR1WWKoMj/LfykQd1uG3FF6n7zy5IfxWw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-2.1.4.tgz", + "integrity": "sha512-ZgxKjQAm9pidq2kChQO2PqKI9OQpLuGD7iPBuyT0gQg3m1+7vvbbkArLBPzJ12CQHVZvqua7n/QGx8pQjJI2Zw==", "cpu": [ "riscv64" ], @@ -3559,9 +3511,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-x64-gnu": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-2.1.0.tgz", - "integrity": "sha512-oE2CMALLdV3QNiA3YYDZ46tDGf+WRlqu/tQ+B79JYKVwt3sI0fpzvgwPNpx/gfRKUyA0phaeYS4kyOEnpltjpA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-2.1.4.tgz", + "integrity": "sha512-C53B3e6M4yzlYn4hDxR9cHZV+HqkfFGR0zuhH8QCfdBfN5KyGsmXmujiFU85ANEvBgf9CF8VreBQeJ20lyto/g==", "cpu": [ "x64" ], @@ -3573,9 +3525,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-x64-musl": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-2.1.0.tgz", - "integrity": "sha512-lxTFZgsfPPyyIt/DpOH5TK2u1ZROMB+gLp/LWvYBc8FSOtmR0Gl4L/AWmJdM2yqwPfy0hgSkVicf/7k80jHuVQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-2.1.4.tgz", + "integrity": "sha512-UaeG3FRo7e5RameQvWRNQ6KeXF29LEk67U/ohb9tF4U38mKH1OGqhmwCf5yLkqiOSgOi7Ff3ireOZD83Fso1iw==", "cpu": [ "x64" ], @@ -3587,9 +3539,9 @@ "peer": true }, "node_modules/@rspack/binding-wasm32-wasi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-2.1.0.tgz", - "integrity": "sha512-ZsDDduXaEF1SpyGz2OFuEU9Tzm0pKtbtCYviymiNtQS+3lx6rXyv+FaK0oIWn+gWL+gVNamplxKnNNR2jZsp5w==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-2.1.4.tgz", + "integrity": "sha512-0P1WZEfu7JOPzD/jQfk9U/6gnRFc7RpvYCQaYZVVYZNJ2gU6O0/yLegRGKNK/2L0zjYwiD0ynhOIBVUUERf5Pw==", "cpu": [ "wasm32" ], @@ -3597,15 +3549,15 @@ "optional": true, "peer": true, "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "1.1.5" + "@emnapi/core": "1.11.2", + "@emnapi/runtime": "1.11.2", + "@napi-rs/wasm-runtime": "1.1.6" } }, "node_modules/@rspack/binding-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", "license": "MIT", "optional": true, "peer": true, @@ -3615,9 +3567,9 @@ } }, "node_modules/@rspack/binding-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", "license": "MIT", "optional": true, "peer": true, @@ -3637,9 +3589,9 @@ } }, "node_modules/@rspack/binding-win32-arm64-msvc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-2.1.0.tgz", - "integrity": "sha512-0uMWAZYgwdkk0ocE4X85w/0BNWT5GaKJTEZDVYxfSYcfVAxJvIsuM0VH/cjRjsQtEeE1rcY7JvJyd0rQ6j0DqA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-2.1.4.tgz", + "integrity": "sha512-+aStQipk1EakLRPfD+/aQbtmTXfxqSWetcRRDWV3cAsD4ebv1tF8FLKYY1PCaFpQbX1FxzCBGF0KHxkAsi9cxA==", "cpu": [ "arm64" ], @@ -3651,9 +3603,9 @@ "peer": true }, "node_modules/@rspack/binding-win32-ia32-msvc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-2.1.0.tgz", - "integrity": "sha512-MRuIZwF6w1tGyZgoJZ5dnpLaD/oMx8zAYSYQfNS7l0f7qjxbnp42625wkeNB8kPqrmDfqaWUWLwiOaRqFPmumA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-2.1.4.tgz", + "integrity": "sha512-7nyrLRQ07j6i6omZuwiwwTI6rpjdy/Su3niLDAG7WsLL21u3/UQQrw6Fvm8SH3XYteghoHLSM3dhgaisuUhdJg==", "cpu": [ "ia32" ], @@ -3665,9 +3617,9 @@ "peer": true }, "node_modules/@rspack/binding-win32-x64-msvc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-2.1.0.tgz", - "integrity": "sha512-Fme2Ifa647CtD7N6we9xvK+COzfzVJREtUayxdG+VArPdijURZyRQVUKKlYSBW+2qMg+G+kF1xo7gf7svG3sNA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-2.1.4.tgz", + "integrity": "sha512-Z4je7JBaDpO9vvNMgCxlycycRJq+GQwwuXSXagW2xvvGM10Ij63YVtIehSMG9xaW8PED41oc9nWndoEsiD60pA==", "cpu": [ "x64" ], @@ -3679,13 +3631,13 @@ "peer": true }, "node_modules/@rspack/core": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@rspack/core/-/core-2.1.0.tgz", - "integrity": "sha512-dlZRzWQi90HzLYErGh0/xnEWAEMEAtDKXvNxERCEj5uIVIOVu9+uYwpNyAkKc9cK5sPhOz05kk9MIb1EaUJ5gg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@rspack/core/-/core-2.1.4.tgz", + "integrity": "sha512-lpJgtr+JAXuDAMBJfRJ1LHyWVuYJyhZu6L6aj9t4lipUU03qwakHnzn3vwSCr68PsVvVPzR6NbJE1gJienSV0g==", "license": "MIT", "peer": true, "dependencies": { - "@rspack/binding": "2.1.0" + "@rspack/binding": "2.1.4" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -4486,9 +4438,9 @@ ] }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "license": "MIT", "optional": true, "dependencies": { @@ -4730,9 +4682,10 @@ } }, "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, "license": "MIT" }, "node_modules/@types/stack-utils": { @@ -5641,16 +5594,16 @@ } }, "node_modules/@xhmikosr/decompress": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@xhmikosr/decompress/-/decompress-11.1.2.tgz", - "integrity": "sha512-f2hlnMN1ChbifAfdzWns6mssojjr3lgJm6MtT4ttVAClx85QEIQAdGXN22bPqy/qKxbvDf93GdqbR6+xIO/a4A==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress/-/decompress-11.1.3.tgz", + "integrity": "sha512-NiyhJq6z7ERsYghcnXZUI6ooDXgZtoB+G9eUsYhfSM4VLp2rKx9UxhKI1NEf1PqosrNPxG3bnSsr2UBVbNurlg==", "dev": true, "license": "MIT", "dependencies": { "@xhmikosr/decompress-tar": "^9.0.1", "@xhmikosr/decompress-tarbz2": "^9.0.1", "@xhmikosr/decompress-targz": "^9.0.1", - "@xhmikosr/decompress-unzip": "^8.1.0", + "@xhmikosr/decompress-unzip": "^8.1.1", "graceful-fs": "^4.2.11", "strip-dirs": "^3.0.0" }, @@ -5894,15 +5847,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "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", - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -7250,18 +7194,6 @@ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", "license": "MIT" }, - "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", - "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", @@ -8542,18 +8474,6 @@ "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==", - "license": "MIT", - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/expect": { "version": "30.4.1", "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", @@ -8770,30 +8690,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "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", - "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==", - "license": "MIT", - "dependencies": { - "find-file-up": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -9218,54 +9114,6 @@ "license": "BSD-2-Clause", "peer": true }, - "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" - }, - "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" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix/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/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", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -9495,18 +9343,6 @@ "react-is": "^16.7.0" } }, - "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", - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -9715,12 +9551,6 @@ "dev": true, "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/inspect-with-kind": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/inspect-with-kind/-/inspect-with-kind-1.0.5.tgz", @@ -10209,15 +10039,6 @@ "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", @@ -11602,12 +11423,6 @@ "dev": true, "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==", - "license": "MIT" - }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -11648,6 +11463,8 @@ "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=12" } @@ -11940,20 +11757,6 @@ "node": ">=18" } }, - "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" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -12371,15 +12174,6 @@ "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/parse5": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", @@ -13130,19 +12924,6 @@ "node": ">=8" } }, - "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", - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -13670,12 +13451,6 @@ "node": ">=0.10.0" } }, - "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/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -14916,16 +14691,6 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" } }, - "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": "*" - } - }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -15701,7 +15466,7 @@ "version": "0.54.0-rc.0", "license": "Apache-2.0", "dependencies": { - "@module-federation/enhanced": "^2.6.0", + "@module-federation/enhanced": "^2.8.0", "@perses-dev/client": "0.54.0-rc.0", "@perses-dev/components": "0.54.0-rc.0", "@perses-dev/core": "0.53.0", @@ -15714,6 +15479,9 @@ "use-query-params": "^2.2.1", "zod": "^3.25.76" }, + "devDependencies": { + "@types/semver": "^7.7.1" + }, "peerDependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", diff --git a/plugin-system/package.json b/plugin-system/package.json index c26e4620..a5a9056e 100644 --- a/plugin-system/package.json +++ b/plugin-system/package.json @@ -28,18 +28,18 @@ "lint:fix": "eslint --fix src --ext .ts,.tsx" }, "dependencies": { - "@module-federation/enhanced": "^2.6.0", + "@module-federation/enhanced": "^2.8.0", + "@perses-dev/client": "0.54.0-rc.0", "@perses-dev/components": "0.54.0-rc.0", "@perses-dev/core": "0.53.0", "@perses-dev/spec": "0.2.0-beta.9", - "@perses-dev/client": "0.54.0-rc.0", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "immer": "^10.1.1", "react-hook-form": "^7.46.1", + "semver": "^7.8.0", "use-query-params": "^2.2.1", - "zod": "^3.25.76", - "semver": "^7.8.0" + "zod": "^3.25.76" }, "peerDependencies": { "@emotion/react": "^11.14.0", @@ -52,5 +52,8 @@ }, "files": [ "dist" - ] + ], + "devDependencies": { + "@types/semver": "^7.7.1" + } } From 100357f4623a467eb4bf262f11666e2503b511fb Mon Sep 17 00:00:00 2001 From: Augustin Husson Date: Tue, 21 Jul 2026 11:23:22 +0200 Subject: [PATCH 12/17] Release v0.54.0-rc.1 (#205) Signed-off-by: Augustin Husson --- client/package.json | 4 +- components/package.json | 6 +- cue/cue.mod/module.cue | 2 +- dashboards/package.json | 10 +- explore/package.json | 8 +- package-lock.json | 112 ++++++++++----------- package.json | 2 +- plugin-system/package.json | 8 +- plugin-system/src/remote/PluginRuntime.tsx | 24 ++--- 9 files changed, 88 insertions(+), 88 deletions(-) diff --git a/client/package.json b/client/package.json index 4211d0cc..242b55a9 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/client", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "description": "Functions as an API client or Data fetching Layer for interacting with a backend service", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -15,7 +15,7 @@ "main": "dist/cjs/index.js", "types": "dist/index.d.ts", "dependencies": { - "@perses-dev/spec": "0.2.0-beta.9", + "@perses-dev/spec": "0.2.0-rc.0", "zod": "^3.21.4" }, "scripts": { diff --git a/components/package.json b/components/package.json index 502a764d..d9dee83b 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/components", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "description": "Common UI components used across Perses features", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -34,8 +34,8 @@ "@date-fns/tz": "^1.4.1", "@fontsource/inter": "^5.0.0", "@mui/x-date-pickers": "^7.23.1", - "@perses-dev/spec": "0.2.0-beta.9", - "@perses-dev/client": "0.54.0-rc.0", + "@perses-dev/spec": "0.2.0-rc.0", + "@perses-dev/client": "0.54.0-rc.1", "numbro": "^2.3.6", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-table": "^8.20.5", diff --git a/cue/cue.mod/module.cue b/cue/cue.mod/module.cue index 1f5a0014..5aef98b8 100644 --- a/cue/cue.mod/module.cue +++ b/cue/cue.mod/module.cue @@ -7,7 +7,7 @@ source: { } deps: { "github.com/perses/spec/cue@v0": { - v: "v0.2.0-beta.9" + v: "v0.2.0-rc.0" default: true } } diff --git a/dashboards/package.json b/dashboards/package.json index cd670181..e17487c2 100644 --- a/dashboards/package.json +++ b/dashboards/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/dashboards", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "description": "The dashboards feature in Perses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -29,10 +29,10 @@ "lint:fix": "eslint --fix src --ext .ts,.tsx" }, "dependencies": { - "@perses-dev/components": "0.54.0-rc.0", - "@perses-dev/plugin-system": "0.54.0-rc.0", - "@perses-dev/spec": "0.2.0-beta.9", - "@perses-dev/client": "0.54.0-rc.0", + "@perses-dev/components": "0.54.0-rc.1", + "@perses-dev/plugin-system": "0.54.0-rc.1", + "@perses-dev/spec": "0.2.0-rc.0", + "@perses-dev/client": "0.54.0-rc.1", "@tanstack/hotkeys": "^0.8.0", "@tanstack/react-hotkeys": "^0.9.1", "immer": "^10.1.1", diff --git a/explore/package.json b/explore/package.json index c3ca19ae..494f7847 100644 --- a/explore/package.json +++ b/explore/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/explore", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "description": "The explore feature in Perses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -28,9 +28,9 @@ }, "dependencies": { "@nexucis/fuzzy": "^0.5.1", - "@perses-dev/components": "0.54.0-rc.0", - "@perses-dev/dashboards": "0.54.0-rc.0", - "@perses-dev/plugin-system": "0.54.0-rc.0", + "@perses-dev/components": "0.54.0-rc.1", + "@perses-dev/dashboards": "0.54.0-rc.1", + "@perses-dev/plugin-system": "0.54.0-rc.1", "mdi-material-ui": "^7.9.2", "qs": "^6.14.0", "react-virtuoso": "^4.12.2", diff --git a/package-lock.json b/package-lock.json index d6f65cc8..491d35c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "perses-shared", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "perses-shared", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "workspaces": [ "components", "dashboards", @@ -55,16 +55,16 @@ }, "client": { "name": "@perses-dev/client", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "license": "Apache-2.0", "dependencies": { - "@perses-dev/spec": "0.2.0-beta.9", + "@perses-dev/spec": "0.2.0-rc.0", "zod": "^3.21.4" } }, "components": { "name": "@perses-dev/components", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "license": "Apache-2.0", "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.4.0", @@ -73,8 +73,8 @@ "@date-fns/tz": "^1.4.1", "@fontsource/inter": "^5.0.0", "@mui/x-date-pickers": "^7.23.1", - "@perses-dev/client": "0.54.0-rc.0", - "@perses-dev/spec": "0.2.0-beta.9", + "@perses-dev/client": "0.54.0-rc.1", + "@perses-dev/spec": "0.2.0-rc.0", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-table": "^8.20.5", "@uiw/react-codemirror": "^4.19.1", @@ -104,13 +104,13 @@ }, "dashboards": { "name": "@perses-dev/dashboards", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "license": "Apache-2.0", "dependencies": { - "@perses-dev/client": "0.54.0-rc.0", - "@perses-dev/components": "0.54.0-rc.0", - "@perses-dev/plugin-system": "0.54.0-rc.0", - "@perses-dev/spec": "0.2.0-beta.9", + "@perses-dev/client": "0.54.0-rc.1", + "@perses-dev/components": "0.54.0-rc.1", + "@perses-dev/plugin-system": "0.54.0-rc.1", + "@perses-dev/spec": "0.2.0-rc.0", "@tanstack/hotkeys": "^0.8.0", "@tanstack/react-hotkeys": "^0.9.1", "immer": "^10.1.1", @@ -176,13 +176,13 @@ }, "explore": { "name": "@perses-dev/explore", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "license": "Apache-2.0", "dependencies": { "@nexucis/fuzzy": "^0.5.1", - "@perses-dev/components": "0.54.0-rc.0", - "@perses-dev/dashboards": "0.54.0-rc.0", - "@perses-dev/plugin-system": "0.54.0-rc.0", + "@perses-dev/components": "0.54.0-rc.1", + "@perses-dev/dashboards": "0.54.0-rc.1", + "@perses-dev/plugin-system": "0.54.0-rc.1", "mdi-material-ui": "^7.9.2", "qs": "^6.14.0", "react-virtuoso": "^4.12.2", @@ -1261,9 +1261,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1340,9 +1340,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -3305,9 +3305,9 @@ "link": true }, "node_modules/@perses-dev/spec": { - "version": "0.2.0-beta.9", - "resolved": "https://registry.npmjs.org/@perses-dev/spec/-/spec-0.2.0-beta.9.tgz", - "integrity": "sha512-hB+xmz5nnFWOypYF8UBzqL8YuHaqKVHzRBVlFj6f88ZtLw3j8bxrnMkQ/bSGwgsrEKzbdKWrCrtraokzl6nccA==", + "version": "0.2.0-rc.0", + "resolved": "https://registry.npmjs.org/@perses-dev/spec/-/spec-0.2.0-rc.0.tgz", + "integrity": "sha512-SDtY/+ziGxp0VIpFwk7rlTqRJbmMvyseHTZSpGBqwltXY9sXFuW5VSA+499yIdFdybptG4FFjURCNGt/HXrRgg==", "license": "Apache-2.0", "dependencies": { "date-fns": "^4.1.0", @@ -4918,9 +4918,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", "dev": true, "license": "MIT", "dependencies": { @@ -6510,9 +6510,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "dev": true, "license": "MIT", "dependencies": { @@ -8006,9 +8006,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -8103,9 +8103,9 @@ } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -8204,9 +8204,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -8305,9 +8305,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -8746,9 +8746,9 @@ } }, "node_modules/flat-cache/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -11172,9 +11172,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, "funding": [ { @@ -14123,9 +14123,9 @@ } }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -15463,14 +15463,14 @@ }, "plugin-system": { "name": "@perses-dev/plugin-system", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "license": "Apache-2.0", "dependencies": { "@module-federation/enhanced": "^2.8.0", - "@perses-dev/client": "0.54.0-rc.0", - "@perses-dev/components": "0.54.0-rc.0", + "@perses-dev/client": "0.54.0-rc.1", + "@perses-dev/components": "0.54.0-rc.1", "@perses-dev/core": "0.53.0", - "@perses-dev/spec": "0.2.0-beta.9", + "@perses-dev/spec": "0.2.0-rc.0", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "immer": "^10.1.1", diff --git a/package.json b/package.json index 74f09add..0b67fa5b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "perses-shared", "description": "Monorepo for the Perses UI shared packages", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "private": true, "scripts": { "build": "turbo run build", diff --git a/plugin-system/package.json b/plugin-system/package.json index a5a9056e..4393ba52 100644 --- a/plugin-system/package.json +++ b/plugin-system/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/plugin-system", - "version": "0.54.0-rc.0", + "version": "0.54.0-rc.1", "description": "The plugin feature in Pereses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -29,10 +29,10 @@ }, "dependencies": { "@module-federation/enhanced": "^2.8.0", - "@perses-dev/client": "0.54.0-rc.0", - "@perses-dev/components": "0.54.0-rc.0", + "@perses-dev/client": "0.54.0-rc.1", + "@perses-dev/components": "0.54.0-rc.1", "@perses-dev/core": "0.53.0", - "@perses-dev/spec": "0.2.0-beta.9", + "@perses-dev/spec": "0.2.0-rc.0", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "immer": "^10.1.1", diff --git a/plugin-system/src/remote/PluginRuntime.tsx b/plugin-system/src/remote/PluginRuntime.tsx index 885d404f..cd1b0a11 100644 --- a/plugin-system/src/remote/PluginRuntime.tsx +++ b/plugin-system/src/remote/PluginRuntime.tsx @@ -91,11 +91,11 @@ const getPluginRuntime = (): ModuleFederation => { }, }, '@perses-dev/spec': { - version: '0.2.0-beta.9', + version: '0.2.0-rc.0', lib: () => require('@perses-dev/spec'), shareConfig: { singleton: true, - requiredVersion: '^0.2.0-beta.9', + requiredVersion: '^0.2.0-rc.0', }, }, '@perses-dev/core': { @@ -107,43 +107,43 @@ const getPluginRuntime = (): ModuleFederation => { }, }, '@perses-dev/client': { - version: '0.54.0-rc.0', + version: '0.54.0-rc.1', lib: () => require('@perses-dev/client'), shareConfig: { singleton: true, - requiredVersion: '^0.54.0-rc.0', + requiredVersion: '^0.54.0-rc.1', }, }, '@perses-dev/components': { - version: '0.54.0-rc.0', + version: '0.54.0-rc.1', lib: () => require('@perses-dev/components'), shareConfig: { singleton: true, - requiredVersion: '^0.54.0-rc.0', + requiredVersion: '^0.54.0-rc.1', }, }, '@perses-dev/plugin-system': { - version: '0.54.0-rc.0', + version: '0.54.0-rc.1', lib: () => require('@perses-dev/plugin-system'), shareConfig: { singleton: true, - requiredVersion: '^0.54.0-rc.0', + requiredVersion: '^0.54.0-rc.1', }, }, '@perses-dev/explore': { - version: '0.54.0-rc.0', + version: '0.54.0-rc.1', lib: () => require('@perses-dev/explore'), shareConfig: { singleton: true, - requiredVersion: '^0.54.0-rc.0', + requiredVersion: '^0.54.0-rc.1', }, }, '@perses-dev/dashboards': { - version: '0.54.0-rc.0', + version: '0.54.0-rc.1', lib: () => require('@perses-dev/dashboards'), shareConfig: { singleton: true, - requiredVersion: '^0.54.0-rc.0', + requiredVersion: '^0.54.0-rc.1', }, }, // Below are the shared modules that are used by the plugins, this can be part of the SDK From 88594e0979f5598924a9451ec61f191e86c5a06d Mon Sep 17 00:00:00 2001 From: Augustin Husson Date: Wed, 29 Jul 2026 11:06:52 +0200 Subject: [PATCH 13/17] Release v0.54.0 (#207) Signed-off-by: Augustin Husson --- client/package.json | 4 +- components/package.json | 6 +- dashboards/package.json | 10 +- explore/package.json | 8 +- package-lock.json | 374 ++++++++++++++++++------------------- package.json | 2 +- plugin-system/package.json | 8 +- 7 files changed, 206 insertions(+), 206 deletions(-) diff --git a/client/package.json b/client/package.json index 242b55a9..b785a229 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/client", - "version": "0.54.0-rc.1", + "version": "0.54.0", "description": "Functions as an API client or Data fetching Layer for interacting with a backend service", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -15,7 +15,7 @@ "main": "dist/cjs/index.js", "types": "dist/index.d.ts", "dependencies": { - "@perses-dev/spec": "0.2.0-rc.0", + "@perses-dev/spec": "0.2.0", "zod": "^3.21.4" }, "scripts": { diff --git a/components/package.json b/components/package.json index d9dee83b..5cc325c7 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/components", - "version": "0.54.0-rc.1", + "version": "0.54.0", "description": "Common UI components used across Perses features", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -34,8 +34,8 @@ "@date-fns/tz": "^1.4.1", "@fontsource/inter": "^5.0.0", "@mui/x-date-pickers": "^7.23.1", - "@perses-dev/spec": "0.2.0-rc.0", - "@perses-dev/client": "0.54.0-rc.1", + "@perses-dev/spec": "0.2.0", + "@perses-dev/client": "0.54.0", "numbro": "^2.3.6", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-table": "^8.20.5", diff --git a/dashboards/package.json b/dashboards/package.json index e17487c2..dd6508c2 100644 --- a/dashboards/package.json +++ b/dashboards/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/dashboards", - "version": "0.54.0-rc.1", + "version": "0.54.0", "description": "The dashboards feature in Perses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -29,10 +29,10 @@ "lint:fix": "eslint --fix src --ext .ts,.tsx" }, "dependencies": { - "@perses-dev/components": "0.54.0-rc.1", - "@perses-dev/plugin-system": "0.54.0-rc.1", - "@perses-dev/spec": "0.2.0-rc.0", - "@perses-dev/client": "0.54.0-rc.1", + "@perses-dev/components": "0.54.0", + "@perses-dev/plugin-system": "0.54.0", + "@perses-dev/spec": "0.2.0", + "@perses-dev/client": "0.54.0", "@tanstack/hotkeys": "^0.8.0", "@tanstack/react-hotkeys": "^0.9.1", "immer": "^10.1.1", diff --git a/explore/package.json b/explore/package.json index 494f7847..1ad193b1 100644 --- a/explore/package.json +++ b/explore/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/explore", - "version": "0.54.0-rc.1", + "version": "0.54.0", "description": "The explore feature in Perses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -28,9 +28,9 @@ }, "dependencies": { "@nexucis/fuzzy": "^0.5.1", - "@perses-dev/components": "0.54.0-rc.1", - "@perses-dev/dashboards": "0.54.0-rc.1", - "@perses-dev/plugin-system": "0.54.0-rc.1", + "@perses-dev/components": "0.54.0", + "@perses-dev/dashboards": "0.54.0", + "@perses-dev/plugin-system": "0.54.0", "mdi-material-ui": "^7.9.2", "qs": "^6.14.0", "react-virtuoso": "^4.12.2", diff --git a/package-lock.json b/package-lock.json index 491d35c6..03e4e9f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "perses-shared", - "version": "0.54.0-rc.1", + "version": "0.54.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "perses-shared", - "version": "0.54.0-rc.1", + "version": "0.54.0", "workspaces": [ "components", "dashboards", @@ -55,16 +55,16 @@ }, "client": { "name": "@perses-dev/client", - "version": "0.54.0-rc.1", + "version": "0.54.0", "license": "Apache-2.0", "dependencies": { - "@perses-dev/spec": "0.2.0-rc.0", + "@perses-dev/spec": "0.2.0", "zod": "^3.21.4" } }, "components": { "name": "@perses-dev/components", - "version": "0.54.0-rc.1", + "version": "0.54.0", "license": "Apache-2.0", "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.4.0", @@ -73,8 +73,8 @@ "@date-fns/tz": "^1.4.1", "@fontsource/inter": "^5.0.0", "@mui/x-date-pickers": "^7.23.1", - "@perses-dev/client": "0.54.0-rc.1", - "@perses-dev/spec": "0.2.0-rc.0", + "@perses-dev/client": "0.54.0", + "@perses-dev/spec": "0.2.0", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-table": "^8.20.5", "@uiw/react-codemirror": "^4.19.1", @@ -104,13 +104,13 @@ }, "dashboards": { "name": "@perses-dev/dashboards", - "version": "0.54.0-rc.1", + "version": "0.54.0", "license": "Apache-2.0", "dependencies": { - "@perses-dev/client": "0.54.0-rc.1", - "@perses-dev/components": "0.54.0-rc.1", - "@perses-dev/plugin-system": "0.54.0-rc.1", - "@perses-dev/spec": "0.2.0-rc.0", + "@perses-dev/client": "0.54.0", + "@perses-dev/components": "0.54.0", + "@perses-dev/plugin-system": "0.54.0", + "@perses-dev/spec": "0.2.0", "@tanstack/hotkeys": "^0.8.0", "@tanstack/react-hotkeys": "^0.9.1", "immer": "^10.1.1", @@ -176,13 +176,13 @@ }, "explore": { "name": "@perses-dev/explore", - "version": "0.54.0-rc.1", + "version": "0.54.0", "license": "Apache-2.0", "dependencies": { "@nexucis/fuzzy": "^0.5.1", - "@perses-dev/components": "0.54.0-rc.1", - "@perses-dev/dashboards": "0.54.0-rc.1", - "@perses-dev/plugin-system": "0.54.0-rc.1", + "@perses-dev/components": "0.54.0", + "@perses-dev/dashboards": "0.54.0", + "@perses-dev/plugin-system": "0.54.0", "mdi-material-ui": "^7.9.2", "qs": "^6.14.0", "react-virtuoso": "^4.12.2", @@ -2079,22 +2079,22 @@ "license": "MIT" }, "node_modules/@module-federation/bridge-react-webpack-plugin": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-2.8.0.tgz", - "integrity": "sha512-7AaaiE4YOXFb+st6xlVDK65aNKZYR9S9ykH0q2mkHAHTgquXciAjypS8JuhmKn7Lob/2rkplMOc4YsGxG3Ng9Q==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-2.8.1.tgz", + "integrity": "sha512-w/+d+OjtzT6sa0b3elBcCw6uw+6l8JDOtMIyWzx1lNNuV9IPhq2pgSLXEVEHdIo77r4zgQAktm9kUfCzjO0VrQ==", "license": "MIT", "dependencies": { - "@module-federation/sdk": "2.8.0" + "@module-federation/sdk": "2.8.1" } }, "node_modules/@module-federation/cli": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/cli/-/cli-2.8.0.tgz", - "integrity": "sha512-yTxdWkCJPPo+IGASz+NqdW13cw3DhjSBEn9r85aYn1ahckDwB1WcZe0OjDWMqCcR6yi0BMLedk0SWrUeUj0fWw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/cli/-/cli-2.8.1.tgz", + "integrity": "sha512-DZo0f3gTN7bKoqw/KM5Kj4qIKeF3bfQwCq7fgf2Gnd+jfZPY4otVy7nbRxFmtdienEhqzcmFFWJUPH/cOkyMqg==", "license": "MIT", "dependencies": { - "@module-federation/dts-plugin": "2.8.0", - "@module-federation/sdk": "2.8.0", + "@module-federation/dts-plugin": "2.8.1", + "@module-federation/sdk": "2.8.1", "commander": "11.1.0", "jiti": "2.4.2" }, @@ -2115,16 +2115,16 @@ } }, "node_modules/@module-federation/dts-plugin": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-2.8.0.tgz", - "integrity": "sha512-defjq4jOWMEfeejezPWLP5sc8kw0O6FqTT7/E5rbZPEVyjB1A0U3ynhW6GDE5/6hk9/TzdbWS+fBNi4MqUOY6Q==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-2.8.1.tgz", + "integrity": "sha512-JM8g76KzhhH44kHvM2JPJ5FIlQDwUNzw0vvq5EDSo/znNUmUEuSrfTssukCKg1nqnBGWLohjIV0LOOhLdCknoQ==", "license": "MIT", "dependencies": { - "@module-federation/error-codes": "2.8.0", - "@module-federation/managers": "2.8.0", - "@module-federation/sdk": "2.8.0", - "@module-federation/third-party-dts-extractor": "2.8.0", - "adm-zip": "0.5.10", + "@module-federation/error-codes": "2.8.1", + "@module-federation/managers": "2.8.1", + "@module-federation/sdk": "2.8.1", + "@module-federation/third-party-dts-extractor": "2.8.1", + "adm-zip": "0.6.0", "isomorphic-ws": "5.0.0", "undici": "7.28.0", "ws": "8.21.0" @@ -2140,22 +2140,22 @@ } }, "node_modules/@module-federation/enhanced": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-2.8.0.tgz", - "integrity": "sha512-h8vkLdhK7tlcSPmyYNGfGyt0pSzfDB0tYVYdyUt2tXwQRfaJAi3bsIpujMXElw3MXtOfSHESa6M/hPKrtWTHBw==", - "license": "MIT", - "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "2.8.0", - "@module-federation/cli": "2.8.0", - "@module-federation/dts-plugin": "2.8.0", - "@module-federation/error-codes": "2.8.0", - "@module-federation/inject-external-runtime-core-plugin": "2.8.0", - "@module-federation/managers": "2.8.0", - "@module-federation/manifest": "2.8.0", - "@module-federation/rspack": "2.8.0", - "@module-federation/runtime-tools": "2.8.0", - "@module-federation/sdk": "2.8.0", - "@module-federation/webpack-bundler-runtime": "2.8.0", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-2.8.1.tgz", + "integrity": "sha512-QHlvm+poXVPkIPK0C8Ras36peZA9CxRp43deSQcArBZ6uEsZTGGm07ohDxRLzVO7kYXR6dE56gIAN9fqa9Fhgg==", + "license": "MIT", + "dependencies": { + "@module-federation/bridge-react-webpack-plugin": "2.8.1", + "@module-federation/cli": "2.8.1", + "@module-federation/dts-plugin": "2.8.1", + "@module-federation/error-codes": "2.8.1", + "@module-federation/inject-external-runtime-core-plugin": "2.8.1", + "@module-federation/managers": "2.8.1", + "@module-federation/manifest": "2.8.1", + "@module-federation/rspack": "2.8.1", + "@module-federation/runtime-tools": "2.8.1", + "@module-federation/sdk": "2.8.1", + "@module-federation/webpack-bundler-runtime": "2.8.1", "schema-utils": "4.3.0", "tapable": "2.3.0" }, @@ -2246,53 +2246,53 @@ } }, "node_modules/@module-federation/error-codes": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-2.8.0.tgz", - "integrity": "sha512-Gaog9904EmxYOQV0hli3XQ7jXeFaADfh5bnBtTCtbZ37Qd/Sz9kQfd+gYQRyIj7RGmkv9DPiN/SsmrTMrTymKw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-2.8.1.tgz", + "integrity": "sha512-0mQ+bWt1LRCZyURx3g2b8G+aAlvk8iXIgrp3Jit/75blrlVda/eVqnHz1L+YOxwkP3xSrdbUb4423AoWti31ZQ==", "license": "MIT" }, "node_modules/@module-federation/inject-external-runtime-core-plugin": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-2.8.0.tgz", - "integrity": "sha512-fW3jD1ZVds6r/Ul8TtUA42RsB0LfT1yjo5KjqgirH9QrmEMH21x44e3e6BC6IN820XKawRDSmz2kFA5YHIQp7Q==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-2.8.1.tgz", + "integrity": "sha512-xpqjWaLw4KbPW4CMRDRiGjH08/ABdPc9z4fRuPiFYA4loNYrjFWALRe2QA6W90SAew/d5RQ6QchO/wuhrzy3dw==", "license": "MIT", "peerDependencies": { - "@module-federation/runtime-tools": "2.8.0" + "@module-federation/runtime-tools": "2.8.1" } }, "node_modules/@module-federation/managers": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-2.8.0.tgz", - "integrity": "sha512-SnVBCwmi962WGg6hLFElxZUCnrRJdR6glE2ZKPBY/iK07AHUN2ZxuaBCBsVzyws+xLZGHZxBHmVstijTh8dSUA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-2.8.1.tgz", + "integrity": "sha512-bHRooIgplIFNLH42eM3g21b2apM/a7lMG1EwifUxT4A4AobS42H08KGdYITdKJcrgowx3D7PhYndiwtHFI03zQ==", "license": "MIT", "dependencies": { - "@module-federation/sdk": "2.8.0" + "@module-federation/sdk": "2.8.1" } }, "node_modules/@module-federation/manifest": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-2.8.0.tgz", - "integrity": "sha512-wfVeBXc4/C2F70nRFSPqJhkcwbDgo+wQyEn3jbjJTDoUqxxhYBfHFs1ACBYOk3Qm97L7hHclHGtUG0/nvDEfAA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-2.8.1.tgz", + "integrity": "sha512-1NOd4J1sJrTpl7M1NYsdUtjSR2Eu3R//r+w51KC9XhAZkxWse0+uPphGdeiUqCOVgAAWjKreckY+xnEmG6Kqig==", "license": "MIT", "dependencies": { - "@module-federation/dts-plugin": "2.8.0", - "@module-federation/managers": "2.8.0", - "@module-federation/sdk": "2.8.0" + "@module-federation/dts-plugin": "2.8.1", + "@module-federation/managers": "2.8.1", + "@module-federation/sdk": "2.8.1" } }, "node_modules/@module-federation/rspack": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-2.8.0.tgz", - "integrity": "sha512-TPcrkHpaZgL25Vx3c8oSNwyv7/KktC7uo6HTQdVWlFzbq5RSoMMGkWoir5pY5124isae2/p6v5xAuqICi4r0Zg==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-2.8.1.tgz", + "integrity": "sha512-jJYkARn1U1M92CbiLyoaP7+tNKh8YzzOTMSGzbdj6okTtWK0Z9ImyVoKEnAnjnLP1nbQjkPEaojkR2D2IQFhLw==", "license": "MIT", "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "2.8.0", - "@module-federation/dts-plugin": "2.8.0", - "@module-federation/inject-external-runtime-core-plugin": "2.8.0", - "@module-federation/managers": "2.8.0", - "@module-federation/manifest": "2.8.0", - "@module-federation/runtime-tools": "2.8.0", - "@module-federation/sdk": "2.8.0" + "@module-federation/bridge-react-webpack-plugin": "2.8.1", + "@module-federation/dts-plugin": "2.8.1", + "@module-federation/inject-external-runtime-core-plugin": "2.8.1", + "@module-federation/managers": "2.8.1", + "@module-federation/manifest": "2.8.1", + "@module-federation/runtime-tools": "2.8.1", + "@module-federation/sdk": "2.8.1" }, "peerDependencies": { "@rspack/core": "^0.7.0 || ^1.0.0 || ^2.0.0-0", @@ -2309,57 +2309,57 @@ } }, "node_modules/@module-federation/runtime": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-2.8.0.tgz", - "integrity": "sha512-cGtUBQ1/TVy7KrXy6xPgy3FEmOGyIYkBA2T4iGH3ZH5PNPPTmqN9jF2AfneTSOj0RtBr7Pxq3CUt81E/UCvK1A==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-2.8.1.tgz", + "integrity": "sha512-+xpq/r6Om4plbGJisZp6/rl7usEUlQszz34E+JUKgl9uW3QIRzVgxwOAzGiF7U+dqOKxMqmiq+nTJwK2wAwteA==", "license": "MIT", "dependencies": { - "@module-federation/error-codes": "2.8.0", - "@module-federation/runtime-core": "2.8.0", - "@module-federation/sdk": "2.8.0" + "@module-federation/error-codes": "2.8.1", + "@module-federation/runtime-core": "2.8.1", + "@module-federation/sdk": "2.8.1" } }, "node_modules/@module-federation/runtime-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-2.8.0.tgz", - "integrity": "sha512-Tf98+epGGiPSHqmQHuXa2uXZMMvjGf1IqJDR1/FpXfmobv5ECN0mGZCjUHGNSyxvoDyXKIkKwJu7IwEoh0ouQA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-2.8.1.tgz", + "integrity": "sha512-Dif+3u7fvq6qBATFIv5qB7ay6Rgo2HNzhaNOt1yRfpVXjiJqQ3UPnHZ+TLP9znkXiZvg6Bg5W9EV2ZX4nH2S0w==", "license": "MIT", "dependencies": { - "@module-federation/error-codes": "2.8.0", - "@module-federation/sdk": "2.8.0" + "@module-federation/error-codes": "2.8.1", + "@module-federation/sdk": "2.8.1" } }, "node_modules/@module-federation/runtime-tools": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-2.8.0.tgz", - "integrity": "sha512-3yOqjdSHXxX4HA3GhlXg3hghGAXW2RJUsnwXCcik2/lTxOHizKI8f3RM+GGCKPxDVqtw43IShe3tA12jNL5A/A==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-2.8.1.tgz", + "integrity": "sha512-CIQ9dPqWOiitXKCYqgHXfr0hehtxsZDfiuFaesJAJnXtqkM5+nVkkBNkY07cDV5wOi00/aiOhEWYoCcz+cRQtQ==", "license": "MIT", "dependencies": { - "@module-federation/runtime": "2.8.0", - "@module-federation/webpack-bundler-runtime": "2.8.0" + "@module-federation/runtime": "2.8.1", + "@module-federation/webpack-bundler-runtime": "2.8.1" } }, "node_modules/@module-federation/sdk": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-2.8.0.tgz", - "integrity": "sha512-yBP+9+0Z8nlvKEXAZS3AsQVy7bFbZf8eMivGk4q4ZdwG3TsLMlsPjb1dQb2i7gcAG6ux9y2LWLkj/0LVk74cnQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-2.8.1.tgz", + "integrity": "sha512-3EVljiNilY2pFIG2RO4KNCC6gIPnYc9J+p5U6Nn8D5X3PtJeEcPyBvKGttxZnSLlXCi+YXQfgexBOfnkvEuzuQ==", "license": "MIT" }, "node_modules/@module-federation/third-party-dts-extractor": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-2.8.0.tgz", - "integrity": "sha512-nAMlr74OKIylkfRwlunOhytQbmsgb3gCqdXWnPQhG+ZtqWXGELLfMT4a1Q1ht3cS+sRpWj2SZRqK2M7GadI6tA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-2.8.1.tgz", + "integrity": "sha512-6ulu7vqv5wyM5YWwxjUHzZ+8Sg6e+/vn+gskiUBs6EPqe/w3XMdRoUrFjAI3EW62xi0e0xbWLsjbQs9jCAj8ig==", "license": "MIT" }, "node_modules/@module-federation/webpack-bundler-runtime": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-2.8.0.tgz", - "integrity": "sha512-82fDy9v+7qV5fiN8TKVhOdrxhmAZnUIX/IKivYX5ulCt8aoOzVFTiwm/P1GQUDD8z6dqR48xgJdZdf0548Mc9w==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-2.8.1.tgz", + "integrity": "sha512-dGFlrTLimhxpHohysx/qPRuIRaAiutqFfX0xFzDchEkY0DXpS2sOhuJ2foNcCIQK/FKFJW1YeaaIUkZ5FWMcOg==", "license": "MIT", "dependencies": { - "@module-federation/error-codes": "2.8.0", - "@module-federation/runtime": "2.8.0", - "@module-federation/sdk": "2.8.0" + "@module-federation/error-codes": "2.8.1", + "@module-federation/runtime": "2.8.1", + "@module-federation/sdk": "2.8.1" } }, "node_modules/@mui/core-downloads-tracker": { @@ -3305,9 +3305,9 @@ "link": true }, "node_modules/@perses-dev/spec": { - "version": "0.2.0-rc.0", - "resolved": "https://registry.npmjs.org/@perses-dev/spec/-/spec-0.2.0-rc.0.tgz", - "integrity": "sha512-SDtY/+ziGxp0VIpFwk7rlTqRJbmMvyseHTZSpGBqwltXY9sXFuW5VSA+499yIdFdybptG4FFjURCNGt/HXrRgg==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@perses-dev/spec/-/spec-0.2.0.tgz", + "integrity": "sha512-0SukMq7kzYBni670E+uSSebr8LzQP4PdgUUli9HVU2FsDTrby5rJ3KjgoGRbCtGJNCB/fmeWGx57ZlfFohvwog==", "license": "Apache-2.0", "dependencies": { "date-fns": "^4.1.0", @@ -3406,30 +3406,30 @@ } }, "node_modules/@rspack/binding": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-2.1.4.tgz", - "integrity": "sha512-iye4BaTYtTt0qa39avWwEsUobVxFNhQHr6B8reFeKU7sdaZsC9LUoDR8JAt5gOnbnzFMPdKgrdU/VzkC6rXbIg==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-2.1.7.tgz", + "integrity": "sha512-wYqi8TY30hsIzLry503o/Uqu7y9Ec7pEwN5TVmB7Pb3xHrR2eHsQPzdpF/GkCLUjQSgD2Es3CDVV1mr6zO/78g==", "license": "MIT", "peer": true, "optionalDependencies": { - "@rspack/binding-darwin-arm64": "2.1.4", - "@rspack/binding-darwin-x64": "2.1.4", - "@rspack/binding-linux-arm64-gnu": "2.1.4", - "@rspack/binding-linux-arm64-musl": "2.1.4", - "@rspack/binding-linux-riscv64-gnu": "2.1.4", - "@rspack/binding-linux-riscv64-musl": "2.1.4", - "@rspack/binding-linux-x64-gnu": "2.1.4", - "@rspack/binding-linux-x64-musl": "2.1.4", - "@rspack/binding-wasm32-wasi": "2.1.4", - "@rspack/binding-win32-arm64-msvc": "2.1.4", - "@rspack/binding-win32-ia32-msvc": "2.1.4", - "@rspack/binding-win32-x64-msvc": "2.1.4" + "@rspack/binding-darwin-arm64": "2.1.7", + "@rspack/binding-darwin-x64": "2.1.7", + "@rspack/binding-linux-arm64-gnu": "2.1.7", + "@rspack/binding-linux-arm64-musl": "2.1.7", + "@rspack/binding-linux-riscv64-gnu": "2.1.7", + "@rspack/binding-linux-riscv64-musl": "2.1.7", + "@rspack/binding-linux-x64-gnu": "2.1.7", + "@rspack/binding-linux-x64-musl": "2.1.7", + "@rspack/binding-wasm32-wasi": "2.1.7", + "@rspack/binding-win32-arm64-msvc": "2.1.7", + "@rspack/binding-win32-ia32-msvc": "2.1.7", + "@rspack/binding-win32-x64-msvc": "2.1.7" } }, "node_modules/@rspack/binding-darwin-arm64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-2.1.4.tgz", - "integrity": "sha512-3Xcs01iw48F4WeE4SHga6bCNb/UEFvtQX4P4eMIaJfGPjTQuxfabGE8yCPm9e3tpLZ5uo+IBnJ6nh5r6tIDOXQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-2.1.7.tgz", + "integrity": "sha512-DwxzrXRctueP/3Pyom9JHcIsRShuEAlHb+mrE5OPT+4cdHI1UnJpbzEvEDLTo4IKJhDb3vjXdHLtjqtL0SYbeA==", "cpu": [ "arm64" ], @@ -3441,9 +3441,9 @@ "peer": true }, "node_modules/@rspack/binding-darwin-x64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-2.1.4.tgz", - "integrity": "sha512-bz/AsCplLs+3fXULPQU9d4r8H4PdeljgHFyItvVIrA/NKZqzQ8sX0topf/zJVZAOtPH7GNnrKjq0/F0U2DHikQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-2.1.7.tgz", + "integrity": "sha512-kPbrYvR/XUHfAMgRVq3QnC71DW/qjwsPj+3hEUuEnRmlploPNy9u8Szf1IHKSVUSrVZBTgDyMoZQdxYLfhResw==", "cpu": [ "x64" ], @@ -3455,9 +3455,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-arm64-gnu": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-2.1.4.tgz", - "integrity": "sha512-x0HQTLU1MusCtNamuXxf3ayEPkvh9uuaq4wVyBqveRkn4FznSOoHUsxTAKMnjGARX+vdLV/y/SwWJRDp2RI4zw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-2.1.7.tgz", + "integrity": "sha512-VFB+YXM3kZ6IIuLV64H3vgnwqvQIIaqfR/aeGwuxYvwcZsrgblSBmXMeDULdgDjqP8Yr0VaFMBBiD9OtG5KdFw==", "cpu": [ "arm64" ], @@ -3469,9 +3469,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-arm64-musl": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-2.1.4.tgz", - "integrity": "sha512-SEYCQD9UflKJMkYGnG5nt2gcsqdkgJsQmryBC/jxo+bOuY9gUSReU99FqCt7WRQrsHLbGeAFeTWs1QzItWG/Bw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-2.1.7.tgz", + "integrity": "sha512-Mzbxyg0aJ+ITj526Iuz0enEDYY6WxhFIwEKXqwjQh+Vpd5v/+aPzPo83sSQVX/3puBV1sbmviTURbh6N9e1fvA==", "cpu": [ "arm64" ], @@ -3483,9 +3483,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-riscv64-gnu": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-2.1.4.tgz", - "integrity": "sha512-jYtQKtnDRaVfyasvTGY04Z7m+xDWZYVwAIEOB4hP7czM7FVLOMgHlMlvw/EgF0DNHrBthqbPfBIS2tP50CzpEA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-2.1.7.tgz", + "integrity": "sha512-mpazwgT/Pse1720mvEJsoXfPkJ+enj0xUqpbe/wL6aedwjGT+9jJNB8HTJXE4XBX0UO7umGqcJMeKA6YsD2CDA==", "cpu": [ "riscv64" ], @@ -3497,9 +3497,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-riscv64-musl": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-2.1.4.tgz", - "integrity": "sha512-ZgxKjQAm9pidq2kChQO2PqKI9OQpLuGD7iPBuyT0gQg3m1+7vvbbkArLBPzJ12CQHVZvqua7n/QGx8pQjJI2Zw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-2.1.7.tgz", + "integrity": "sha512-oU/l3soPRsDEWn7KZic+npyTMM2N1kRdHjoJ+L5IUBXs8bjdTXPLoyTbTdIOza5ZSoT4+UeEiEryj4BB0tQE5w==", "cpu": [ "riscv64" ], @@ -3511,9 +3511,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-x64-gnu": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-2.1.4.tgz", - "integrity": "sha512-C53B3e6M4yzlYn4hDxR9cHZV+HqkfFGR0zuhH8QCfdBfN5KyGsmXmujiFU85ANEvBgf9CF8VreBQeJ20lyto/g==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-2.1.7.tgz", + "integrity": "sha512-7Gtpl3h3jtnOpk1mYQE8mRndXAO2ibI8mnAbs7klevdKey+ZHneWMoMi2yOMQhhI/ifWEFxDzyGJ8bdxo0XTsA==", "cpu": [ "x64" ], @@ -3525,9 +3525,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-x64-musl": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-2.1.4.tgz", - "integrity": "sha512-UaeG3FRo7e5RameQvWRNQ6KeXF29LEk67U/ohb9tF4U38mKH1OGqhmwCf5yLkqiOSgOi7Ff3ireOZD83Fso1iw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-2.1.7.tgz", + "integrity": "sha512-w+whI2Uy+DYkGN+MVkzMFWweL7B/s1gMqX+nvTE1vhOy3hGV0VyA9H6lqWjSD3I+eGkpYhN9Pr244cYnLpZOUQ==", "cpu": [ "x64" ], @@ -3539,9 +3539,9 @@ "peer": true }, "node_modules/@rspack/binding-wasm32-wasi": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-2.1.4.tgz", - "integrity": "sha512-0P1WZEfu7JOPzD/jQfk9U/6gnRFc7RpvYCQaYZVVYZNJ2gU6O0/yLegRGKNK/2L0zjYwiD0ynhOIBVUUERf5Pw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-2.1.7.tgz", + "integrity": "sha512-cDVgvzRdTgxaeM+a5Lx0+7/VAvunvwO0wNtQ3ATQGOtFCW5b7cUzhNPcytH5ZSJTnFWuxinlGwtar5yfcnkdZQ==", "cpu": [ "wasm32" ], @@ -3589,9 +3589,9 @@ } }, "node_modules/@rspack/binding-win32-arm64-msvc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-2.1.4.tgz", - "integrity": "sha512-+aStQipk1EakLRPfD+/aQbtmTXfxqSWetcRRDWV3cAsD4ebv1tF8FLKYY1PCaFpQbX1FxzCBGF0KHxkAsi9cxA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-2.1.7.tgz", + "integrity": "sha512-JDd85+iYwUvaG9Zrt5X7oIxRZRiTW+76FwkRakoXNy/5VAWQW32Jq4ESjSVz6l6mh0KnZxPq3TLMugacCPnLjw==", "cpu": [ "arm64" ], @@ -3603,9 +3603,9 @@ "peer": true }, "node_modules/@rspack/binding-win32-ia32-msvc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-2.1.4.tgz", - "integrity": "sha512-7nyrLRQ07j6i6omZuwiwwTI6rpjdy/Su3niLDAG7WsLL21u3/UQQrw6Fvm8SH3XYteghoHLSM3dhgaisuUhdJg==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-2.1.7.tgz", + "integrity": "sha512-y9PKEs6v9BLHV0i/4eaIRtxpATvSgcf/VYQkMT8mp+qWlPjUwDQNwU2ueWVGpff6INO+YAa7zobzziNFRgO7Lg==", "cpu": [ "ia32" ], @@ -3617,9 +3617,9 @@ "peer": true }, "node_modules/@rspack/binding-win32-x64-msvc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-2.1.4.tgz", - "integrity": "sha512-Z4je7JBaDpO9vvNMgCxlycycRJq+GQwwuXSXagW2xvvGM10Ij63YVtIehSMG9xaW8PED41oc9nWndoEsiD60pA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-2.1.7.tgz", + "integrity": "sha512-BjkOzcPY/K8YlRRvyywz0mDWk89MMxqAMhDmgBXCWorh1IjgKTsWDJ2lCGIM8M9CZXUG3khom8AfrOGwRT2I+g==", "cpu": [ "x64" ], @@ -3631,13 +3631,13 @@ "peer": true }, "node_modules/@rspack/core": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@rspack/core/-/core-2.1.4.tgz", - "integrity": "sha512-lpJgtr+JAXuDAMBJfRJ1LHyWVuYJyhZu6L6aj9t4lipUU03qwakHnzn3vwSCr68PsVvVPzR6NbJE1gJienSV0g==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@rspack/core/-/core-2.1.7.tgz", + "integrity": "sha512-d5Ju3zXzGgbqQWvlMlLUtek2eFPIzsFe2QOF4nwTAknxo/4OZ64t+kPT9nM6fr3aZX93VK0R3v02/kZYIRrV9Q==", "license": "MIT", "peer": true, "dependencies": { - "@rspack/binding": "2.1.4" + "@rspack/binding": "2.1.7" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -4918,16 +4918,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { @@ -5773,12 +5773,12 @@ } }, "node_modules/adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.0.tgz", + "integrity": "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==", "license": "MIT", "engines": { - "node": ">=6.0" + "node": ">=14.0" } }, "node_modules/agent-base": { @@ -6510,9 +6510,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", - "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", + "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", "dev": true, "license": "MIT", "dependencies": { @@ -6954,15 +6954,15 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.3.tgz", - "integrity": "sha512-hc3LH4UaKWd/bbyDK/IGVa4RB6PtQ3CUYwtrkzqHn+wIG3Hr5fhpRlk0L/gCa8ZE1L/Ufj50Zho69cI5w8SQBA==", + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.4.tgz", + "integrity": "sha512-trZql+7l/0+WRAsAnEdctr4+iiOS6ZrViI6H8QWcCF9MFS/LT0dKpe8vluB1to6it+OxSI4VospFTIFMW8DJRw==", "dev": true, "license": "MIT", "dependencies": { "chalk": "5.6.2", "rxjs": "7.8.2", - "shell-quote": "1.8.4", + "shell-quote": "1.9.0", "supports-color": "10.2.2", "tree-kill": "1.2.2", "yargs": "18.0.0" @@ -8560,9 +8560,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", @@ -13308,9 +13308,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", - "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz", + "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==", "dev": true, "license": "MIT", "engines": { @@ -15463,14 +15463,14 @@ }, "plugin-system": { "name": "@perses-dev/plugin-system", - "version": "0.54.0-rc.1", + "version": "0.54.0", "license": "Apache-2.0", "dependencies": { "@module-federation/enhanced": "^2.8.0", - "@perses-dev/client": "0.54.0-rc.1", - "@perses-dev/components": "0.54.0-rc.1", + "@perses-dev/client": "0.54.0", + "@perses-dev/components": "0.54.0", "@perses-dev/core": "0.53.0", - "@perses-dev/spec": "0.2.0-rc.0", + "@perses-dev/spec": "0.2.0", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "immer": "^10.1.1", diff --git a/package.json b/package.json index 0b67fa5b..7d5add98 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "perses-shared", "description": "Monorepo for the Perses UI shared packages", - "version": "0.54.0-rc.1", + "version": "0.54.0", "private": true, "scripts": { "build": "turbo run build", diff --git a/plugin-system/package.json b/plugin-system/package.json index 4393ba52..7554c0cb 100644 --- a/plugin-system/package.json +++ b/plugin-system/package.json @@ -1,6 +1,6 @@ { "name": "@perses-dev/plugin-system", - "version": "0.54.0-rc.1", + "version": "0.54.0", "description": "The plugin feature in Pereses", "license": "Apache-2.0", "homepage": "https://github.com/perses/perses/blob/main/README.md", @@ -29,10 +29,10 @@ }, "dependencies": { "@module-federation/enhanced": "^2.8.0", - "@perses-dev/client": "0.54.0-rc.1", - "@perses-dev/components": "0.54.0-rc.1", + "@perses-dev/client": "0.54.0", + "@perses-dev/components": "0.54.0", "@perses-dev/core": "0.53.0", - "@perses-dev/spec": "0.2.0-rc.0", + "@perses-dev/spec": "0.2.0", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "immer": "^10.1.1", From 40f4486a8dd07eb2a060a1773a074d6fc7412c00 Mon Sep 17 00:00:00 2001 From: Gabriel Bernal Date: Thu, 30 Jul 2026 14:55:06 +0200 Subject: [PATCH 14/17] [ENHANCEMENT] include dashboard tags into v1alpha2 CR export (#209) Signed-off-by: Gabriel Bernal --- .../DownloadButton/serializeDashboard.ts | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/dashboards/src/components/DownloadButton/serializeDashboard.ts b/dashboards/src/components/DownloadButton/serializeDashboard.ts index 5b0754cf..a33d5f30 100644 --- a/dashboards/src/components/DownloadButton/serializeDashboard.ts +++ b/dashboards/src/components/DownloadButton/serializeDashboard.ts @@ -12,6 +12,7 @@ // limitations under the License. import { DashboardResource } from '@perses-dev/client'; +import { DashboardSpec } from '@perses-dev/spec'; import { stringify } from 'yaml'; //TODO: Although the previous comment suggests the metadata not should not be used, I keep them. Need to be discussed. @@ -22,6 +23,20 @@ type SerializedDashboard = { content: string; }; +type PersesV1Alpha2CR = { + apiVersion: 'perses.dev/v1alpha2'; + kind: 'PersesDashboard'; + metadata: { + labels: Record; + annotations?: Record; + name: string; + namespace: string; + }; + spec: { + config: DashboardSpec; + }; +}; + function serializeYaml(dashboard: DashboardResource, shape?: 'cr-v1alpha1' | 'cr-v1alpha2'): SerializedDashboard { let content: string; @@ -46,25 +61,28 @@ function serializeYaml(dashboard: DashboardResource, shape?: 'cr-v1alpha1' | 'cr ); } else if (shape === 'cr-v1alpha2') { const name = dashboard.metadata.name.toLowerCase().replace(/[^a-z0-9-]/g, '-'); - content = stringify( - { - apiVersion: 'perses.dev/v1alpha2', - kind: 'PersesDashboard', - metadata: { - labels: { - 'app.kubernetes.io/name': 'perses-dashboard', - 'app.kubernetes.io/instance': name, - 'app.kubernetes.io/part-of': 'perses-operator', - }, - name, - namespace: dashboard.metadata.project, - }, - spec: { - config: dashboard.spec, + const crContent: PersesV1Alpha2CR = { + apiVersion: 'perses.dev/v1alpha2', + kind: 'PersesDashboard', + metadata: { + labels: { + 'app.kubernetes.io/name': 'perses-dashboard', + 'app.kubernetes.io/instance': name, + 'app.kubernetes.io/part-of': 'perses-operator', }, + name, + namespace: dashboard.metadata.project, }, - { schema: 'yaml-1.1' } - ); + spec: { + config: dashboard.spec, + }, + }; + + if (dashboard.metadata.tags && dashboard.metadata.tags.length > 0) { + crContent.metadata.annotations = { 'perses.dev/tags': dashboard.metadata.tags.join(',') }; + } + + content = stringify(crContent, { schema: 'yaml-1.1' }); } else { content = stringify(dashboard, { schema: 'yaml-1.1' }); } From ba74808852bc75bd0c9dce596ad545f44a2eb1ca Mon Sep 17 00:00:00 2001 From: Gabriel Bernal Date: Mon, 3 Aug 2026 11:52:22 +0200 Subject: [PATCH 15/17] [BUGFIX] fallback to versionless plugin export (#211) Signed-off-by: Gabriel Bernal --- .../src/components/PluginRegistry/PluginRegistry.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugin-system/src/components/PluginRegistry/PluginRegistry.tsx b/plugin-system/src/components/PluginRegistry/PluginRegistry.tsx index 5fc6f631..fbee2fd8 100644 --- a/plugin-system/src/components/PluginRegistry/PluginRegistry.tsx +++ b/plugin-system/src/components/PluginRegistry/PluginRegistry.tsx @@ -77,8 +77,12 @@ export function PluginRegistry(props: PluginRegistryProps): ReactElement { if (!resource) continue; const pluginModule = (await loadPluginModule(resource)) as Record>; + // Try to get the plugin implementation from the module using the versioned export first const plugin = pluginModule?.[resourceKey]; if (plugin) return plugin as PluginImplementation; + // If the plugin module doesn't have a versioned export, fallback to the plugin name + const versionlessPlugin = pluginModule?.[name]; + if (versionlessPlugin) return versionlessPlugin as PluginImplementation; } throw new Error(`A ${name} plugin for kind '${kind}' is not installed`); From 43d84debf938897184846e11c23842d54b0d68b9 Mon Sep 17 00:00:00 2001 From: Gabriel Bernal Date: Wed, 5 Aug 2026 15:23:51 +0200 Subject: [PATCH 16/17] [FEATURE] add fetch provider (#214) Signed-off-by: Gabriel Bernal --- client/package.json | 3 + client/src/context/FetchContext.test.tsx | 90 +++++++++++++++++++ client/src/context/FetchContext.tsx | 45 ++++++++++ client/src/context/index.ts | 14 +++ client/src/index.ts | 1 + .../Panel/useSelectionItemActions.tsx | 6 +- package-lock.json | 3 + .../src/remote/remotePluginLoader.test.ts | 13 +++ .../src/remote/remotePluginLoader.ts | 8 +- .../src/runtime/UsageMetricsProvider.tsx | 8 +- plugin-system/src/runtime/item-actions.ts | 25 +++--- 11 files changed, 202 insertions(+), 14 deletions(-) create mode 100644 client/src/context/FetchContext.test.tsx create mode 100644 client/src/context/FetchContext.tsx create mode 100644 client/src/context/index.ts diff --git a/client/package.json b/client/package.json index b785a229..06d74d12 100644 --- a/client/package.json +++ b/client/package.json @@ -18,6 +18,9 @@ "@perses-dev/spec": "0.2.0", "zod": "^3.21.4" }, + "peerDependencies": { + "react": "^17.0.2 || ^18.0.0" + }, "scripts": { "clean": "rimraf dist/", "build": "concurrently \"npm:build:*\"", diff --git a/client/src/context/FetchContext.test.tsx b/client/src/context/FetchContext.test.tsx new file mode 100644 index 00000000..fff34488 --- /dev/null +++ b/client/src/context/FetchContext.test.tsx @@ -0,0 +1,90 @@ +// Copyright 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, waitFor } from '@testing-library/react'; +import { FetchProvider, useFetch, FetchFn } from './FetchContext'; + +function TestConsumer(): React.ReactElement { + const { fetch } = useFetch(); + return ; +} + +function TestJsonConsumer({ url }: { url: string }): React.ReactElement { + const { fetchJson } = useFetch(); + return ( + + ); +} + +describe('FetchContext', () => { + describe('useFetch without provider', () => { + it('returns the default fetch wrapper from @perses-dev/client', () => { + let hookResult: ReturnType | undefined; + function Capture(): React.ReactNode { + hookResult = useFetch(); + return null; + } + render(); + expect(hookResult).toBeDefined(); + expect(typeof hookResult!.fetch).toBe('function'); + expect(typeof hookResult!.fetchJson).toBe('function'); + }); + }); + + describe('FetchProvider with custom fetchFn', () => { + it('provides the custom fetch to useFetch consumers', async () => { + const customFetch: FetchFn = jest.fn().mockResolvedValue({ + ok: true, + } as unknown as Response); + + render( + + + + ); + + screen.getByText('fire').click(); + + await waitFor(() => { + expect(customFetch).toHaveBeenCalledWith('/test'); + }); + }); + + it('derives fetchJson from the custom fetch', async () => { + const customFetch: FetchFn = jest.fn().mockResolvedValue({ + ok: true, + json: jest.fn().mockResolvedValue({ ok: true }), + } as unknown as Response); + + render( + + + + ); + + screen.getByText('json').click(); + + await waitFor(() => { + expect(customFetch).toHaveBeenCalledWith('/api/data'); + expect(document.title).toBe('{"ok":true}'); + }); + }); + }); +}); diff --git a/client/src/context/FetchContext.tsx b/client/src/context/FetchContext.tsx new file mode 100644 index 00000000..b3ae5906 --- /dev/null +++ b/client/src/context/FetchContext.tsx @@ -0,0 +1,45 @@ +// Copyright 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, ReactNode, useCallback, useContext } from 'react'; +import { fetch as defaultFetch } from '../util/fetch'; + +export type FetchFn = (...args: Parameters) => Promise; + +const FetchContext = createContext(defaultFetch); + +export interface FetchProviderProps { + fetchFn: FetchFn; + children: ReactNode; +} + +export function FetchProvider({ fetchFn, children }: FetchProviderProps): ReactElement { + return {children}; +} + +export function useFetch(): { + fetch: FetchFn; + fetchJson: (...args: Parameters) => Promise; +} { + const fetch = useContext(FetchContext); + + const fetchJson = useCallback( + async (...args: Parameters): Promise => { + const response = await fetch(...args); + return await response.json(); + }, + [fetch] + ); + + return { fetch, fetchJson }; +} diff --git a/client/src/context/index.ts b/client/src/context/index.ts new file mode 100644 index 00000000..5a5c0fff --- /dev/null +++ b/client/src/context/index.ts @@ -0,0 +1,14 @@ +// Copyright 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 './FetchContext'; diff --git a/client/src/index.ts b/client/src/index.ts index a3861034..d7179afc 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -14,3 +14,4 @@ export * from './util'; export * from './model'; export * from './schema'; +export * from './context'; diff --git a/dashboards/src/components/Panel/useSelectionItemActions.tsx b/dashboards/src/components/Panel/useSelectionItemActions.tsx index 5f5bbe6d..70c7e0c0 100644 --- a/dashboards/src/components/Panel/useSelectionItemActions.tsx +++ b/dashboards/src/components/Panel/useSelectionItemActions.tsx @@ -14,6 +14,7 @@ import { Box, CircularProgress } from '@mui/material'; import { Dialog, InfoTooltip, useItemActions, useSelection } from '@perses-dev/components'; import { ACTION_ICONS, executeAction, ItemAction, VariableStateMap } from '@perses-dev/plugin-system'; +import { useFetch } from '@perses-dev/client'; import { ReactNode, useCallback, useMemo, useState } from 'react'; import { HeaderIconButton } from './HeaderIconButton'; @@ -41,6 +42,7 @@ export function useSelectionItemActions({ }: UseItemActionsOptions): UseItemActionsResult { const { selectionMap } = useSelection(); const { actionStatuses, setActionStatus } = useItemActions(); + const { fetch } = useFetch(); const [confirmState, setConfirmState] = useState<{ open: boolean; action?: ItemAction; @@ -56,6 +58,7 @@ export function useSelectionItemActions({ selectionMap: new Map>([[item.id, item.data]]), variableState, setActionStatus, + fetchFn: fetch, }); } else { await executeAction({ @@ -63,10 +66,11 @@ export function useSelectionItemActions({ selectionMap: selectionMap as Map>, variableState, setActionStatus, + fetchFn: fetch, }); } }, - [selectionMap, variableState, setActionStatus] + [selectionMap, variableState, setActionStatus, fetch] ); const handleActionClick = useCallback( diff --git a/package-lock.json b/package-lock.json index 03e4e9f1..7030a820 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,6 +60,9 @@ "dependencies": { "@perses-dev/spec": "0.2.0", "zod": "^3.21.4" + }, + "peerDependencies": { + "react": "^18.2.0" } }, "components": { diff --git a/plugin-system/src/remote/remotePluginLoader.test.ts b/plugin-system/src/remote/remotePluginLoader.test.ts index 72f4972c..6338fedc 100644 --- a/plugin-system/src/remote/remotePluginLoader.test.ts +++ b/plugin-system/src/remote/remotePluginLoader.test.ts @@ -119,6 +119,19 @@ describe('remotePluginLoader', () => { expect(result).toEqual([]); expect(mockConsoleError).toHaveBeenCalledWith('RemotePluginLoader: No valid plugins found'); }); + + it('should use custom fetchFn when provided', async () => { + const customFetch = jest.fn().mockResolvedValue({ + json: jest.fn().mockResolvedValue([MOCK_VALID_PLUGIN_MODULE_RESOURCE]), + }); + + const loader = remotePluginLoader({ fetchFn: customFetch }); + const result = await loader.getInstalledPlugins(); + + expect(customFetch).toHaveBeenCalledWith('/api/v1/plugins'); + expect(mockFetch).not.toHaveBeenCalled(); + expect(result).toEqual([MOCK_VALID_PLUGIN_MODULE_RESOURCE]); + }); }); describe('importPluginModule', () => { diff --git a/plugin-system/src/remote/remotePluginLoader.ts b/plugin-system/src/remote/remotePluginLoader.ts index 752e47a7..a933582a 100644 --- a/plugin-system/src/remote/remotePluginLoader.ts +++ b/plugin-system/src/remote/remotePluginLoader.ts @@ -18,6 +18,7 @@ import { PluginType, getPluginModuleCompoundKey, } from '@perses-dev/plugin-system'; +import { fetch as defaultFetch, type FetchFn } from '@perses-dev/client'; import { RemotePluginModule } from './PersesPlugin.types'; import { loadPlugin } from './PluginRuntime'; @@ -58,6 +59,10 @@ type RemotePluginLoaderOptions = { * @default '' **/ baseURL?: string; + /** + * Optional custom fetch function to use for network requests. If not provided, the default fetch implementation will be used. + */ + fetchFn?: FetchFn; }; type ParsedPluginOptions = { @@ -88,10 +93,11 @@ const paramToOptions = (options?: RemotePluginLoaderOptions): ParsedPluginOption */ export function remotePluginLoader(options?: RemotePluginLoaderOptions): PluginLoader { const { pluginsApiPath, pluginsAssetsPath } = paramToOptions(options); + const fetchFn = options?.fetchFn ?? defaultFetch; return { getInstalledPlugins: async (): Promise => { - const pluginsResponse = await fetch(pluginsApiPath); + const pluginsResponse = await fetchFn(pluginsApiPath); const plugins = await pluginsResponse.json(); let pluginModules: PluginModuleResource[] = []; diff --git a/plugin-system/src/runtime/UsageMetricsProvider.tsx b/plugin-system/src/runtime/UsageMetricsProvider.tsx index cf26cd2e..0eb38cd6 100644 --- a/plugin-system/src/runtime/UsageMetricsProvider.tsx +++ b/plugin-system/src/runtime/UsageMetricsProvider.tsx @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { fetch } from '@perses-dev/client'; +import { FetchFn, useFetch } from '@perses-dev/client'; import { QueryDefinition } from '@perses-dev/spec'; import { createContext, ReactElement, ReactNode, useContext } from 'react'; @@ -25,6 +25,7 @@ interface UsageMetrics { renderErrorCount: number; pendingQueries: Map; apiPrefix?: string; + fetchFn: FetchFn; } interface UsageMetricsProps { @@ -76,7 +77,7 @@ export const useUsageMetrics = (): UseUsageMetricsResults => { }; const submitMetrics = async (stats: UsageMetrics): Promise => { - await fetch(`${stats.apiPrefix ?? ''}/api/v1/view`, { + await stats.fetchFn(`${stats.apiPrefix ?? ''}/api/v1/view`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -91,6 +92,8 @@ const submitMetrics = async (stats: UsageMetrics): Promise => { }; export const UsageMetricsProvider = ({ apiPrefix, project, dashboard, children }: UsageMetricsProps): ReactElement => { + const { fetch } = useFetch(); + const ctx: UsageMetrics = { project: project, dashboard: dashboard, @@ -99,6 +102,7 @@ export const UsageMetricsProvider = ({ apiPrefix, project, dashboard, children } renderDurationMs: 0, pendingQueries: new Map(), apiPrefix, + fetchFn: fetch, }; return {children}; diff --git a/plugin-system/src/runtime/item-actions.ts b/plugin-system/src/runtime/item-actions.ts index fb8d7313..97154082 100644 --- a/plugin-system/src/runtime/item-actions.ts +++ b/plugin-system/src/runtime/item-actions.ts @@ -18,7 +18,7 @@ import { SelectionItem, VariableStateMap, } from '@perses-dev/components'; -import { fetch } from '@perses-dev/client'; +import { type FetchFn } from '@perses-dev/client'; import { ItemAction, EventAction, WebhookAction } from '../components/ItemSelectionActionsOptionsEditor'; const BODY_METHODS = new Set(['POST', 'PUT', 'PATCH']); @@ -49,6 +49,8 @@ export interface ExecuteActionParams { variableState?: VariableStateMap; /** Callback to update action status */ setActionStatus: (actionName: string, status: Partial, itemId?: Id) => void; + /** Fetch function from FetchProvider context */ + fetchFn: FetchFn; } /** @@ -163,7 +165,8 @@ async function executeWebhookIndividual( action: WebhookAction, selectionMap: Map, variableState: VariableStateMap | undefined, - setActionStatus: ExecuteActionParams['setActionStatus'] + setActionStatus: ExecuteActionParams['setActionStatus'], + fetchFn: FetchFn ): Promise { const entries = Array.from(selectionMap.entries()); const count = entries.length; @@ -192,7 +195,7 @@ async function executeWebhookIndividual( } // Make the request - const response = await fetch(urlResult.text, { + const response = await fetchFn(urlResult.text, { method: action.method, headers: buildWebhookHeaders(action), body: body, @@ -237,7 +240,8 @@ async function executeWebhookBatch( action: WebhookAction, selectionMap: Map, variableState: VariableStateMap | undefined, - setActionStatus: ExecuteActionParams['setActionStatus'] + setActionStatus: ExecuteActionParams['setActionStatus'], + fetchFn: FetchFn ): Promise { const items = Array.from(selectionMap.values()); @@ -257,7 +261,7 @@ async function executeWebhookBatch( } // Make the request - const response = await fetch(urlResult.text, { + const response = await fetchFn(urlResult.text, { method: action.method, headers: buildWebhookHeaders(action), body: body, @@ -283,12 +287,13 @@ async function executeWebhookAction( action: WebhookAction, selectionMap: Map, variableState: VariableStateMap | undefined, - setActionStatus: ExecuteActionParams['setActionStatus'] + setActionStatus: ExecuteActionParams['setActionStatus'], + fetchFn: FetchFn ): Promise { if (action.batchMode === 'batch') { - return executeWebhookBatch(action, selectionMap, variableState, setActionStatus); + return executeWebhookBatch(action, selectionMap, variableState, setActionStatus, fetchFn); } else { - return executeWebhookIndividual(action, selectionMap, variableState, setActionStatus); + return executeWebhookIndividual(action, selectionMap, variableState, setActionStatus, fetchFn); } } @@ -315,7 +320,7 @@ async function executeEventAction( * @returns Promise resolving to the execution result */ export async function executeAction(params: ExecuteActionParams): Promise { - const { action, selectionMap, variableState, setActionStatus } = params; + const { action, selectionMap, variableState, setActionStatus, fetchFn } = params; if (selectionMap.size === 0) { return { success: true }; @@ -324,7 +329,7 @@ export async function executeAction(params: ExecuteActionParams Date: Thu, 9 Jul 2026 08:34:20 -0400 Subject: [PATCH 17/17] [FEATURE] create @perses-dev/design-tokens package (#198) * [FEATURE] create @perses-dev/design-token package Signed-off-by: Jenny Zhu * [IGNORE] address comments perses/shared pr #198 Signed-off-by: Jenny Zhu --------- Signed-off-by: Jenny Zhu --- design-tokens/.eslintrc.js | 14 ++ design-tokens/jest.config.ts | 21 +++ design-tokens/package.json | 48 ++++++ design-tokens/src/colors.ts | 129 ++++++++++++++++ design-tokens/src/css/index.css | 20 +++ design-tokens/src/css/reset.css | 28 ++++ design-tokens/src/css/semantic.css | 162 +++++++++++++++++++++ design-tokens/src/css/tokens.css | 155 ++++++++++++++++++++ design-tokens/src/index.ts | 16 ++ design-tokens/src/test/consistency.test.ts | 60 ++++++++ design-tokens/src/test/css.test.ts | 115 +++++++++++++++ design-tokens/src/test/tokens.test.ts | 132 +++++++++++++++++ design-tokens/src/test/type-assertions.ts | 62 ++++++++ design-tokens/src/tokens.ts | 132 +++++++++++++++++ design-tokens/src/types.ts | 87 +++++++++++ design-tokens/tsconfig.build.json | 9 ++ design-tokens/tsconfig.json | 8 + jest.shared.ts | 2 +- package-lock.json | 10 ++ package.json | 1 + 20 files changed, 1210 insertions(+), 1 deletion(-) create mode 100644 design-tokens/.eslintrc.js create mode 100644 design-tokens/jest.config.ts create mode 100644 design-tokens/package.json create mode 100644 design-tokens/src/colors.ts create mode 100644 design-tokens/src/css/index.css create mode 100644 design-tokens/src/css/reset.css create mode 100644 design-tokens/src/css/semantic.css create mode 100644 design-tokens/src/css/tokens.css create mode 100644 design-tokens/src/index.ts create mode 100644 design-tokens/src/test/consistency.test.ts create mode 100644 design-tokens/src/test/css.test.ts create mode 100644 design-tokens/src/test/tokens.test.ts create mode 100644 design-tokens/src/test/type-assertions.ts create mode 100644 design-tokens/src/tokens.ts create mode 100644 design-tokens/src/types.ts create mode 100644 design-tokens/tsconfig.build.json create mode 100644 design-tokens/tsconfig.json diff --git a/design-tokens/.eslintrc.js b/design-tokens/.eslintrc.js new file mode 100644 index 00000000..08ac9603 --- /dev/null +++ b/design-tokens/.eslintrc.js @@ -0,0 +1,14 @@ +// Copyright 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 = require('../.eslintrc.base.js'); diff --git a/design-tokens/jest.config.ts b/design-tokens/jest.config.ts new file mode 100644 index 00000000..02371308 --- /dev/null +++ b/design-tokens/jest.config.ts @@ -0,0 +1,21 @@ +// Copyright 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, +}; + +export default jestConfig; diff --git a/design-tokens/package.json b/design-tokens/package.json new file mode 100644 index 00000000..fbd18540 --- /dev/null +++ b/design-tokens/package.json @@ -0,0 +1,48 @@ +{ + "name": "@perses-dev/design-tokens", + "version": "0.54.0-beta.10", + "description": "Perses design tokens for ui components", + "license": "Apache-2.0", + "homepage": "https://github.com/perses/perses/blob/main/README.md", + "repository": { + "type": "git", + "url": "git+https://github.com/perses/perses.git" + }, + "bugs": { + "url": "https://github.com/perses/perses/issues" + }, + "module": "dist/index.js", + "main": "dist/cjs/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/cjs/index.js" + }, + "./css": "./dist/css/index.css", + "./css/reset": "./dist/css/reset.css", + "./css/tokens": "./dist/css/tokens.css", + "./css/semantic": "./dist/css/semantic.css" + }, + "sideEffects": [ + "*.css" + ], + "scripts": { + "clean": "rimraf dist/", + "build": "concurrently \"npm:build:*\"", + "build:cjs": "swc ./src -d dist/cjs --strip-leading-paths --config-file ../.cjs.swcrc --ignore '**/test/**'", + "build:esm": "swc ./src -d dist --strip-leading-paths --config-file ../.swcrc --ignore '**/test/**'", + "build:types": "tsc --project tsconfig.build.json", + "build:css": "mkdir -p dist/css && cp -f src/css/*.css dist/css/", + "type-check": "tsc --noEmit", + "start": "concurrently -P \"npm:build:* -- {*}\" -- --watch", + "test": "cross-env TZ=UTC jest", + "test:watch": "npm run test -- --watch", + "lint": "eslint src --ext .ts,.tsx", + "lint:fix": "eslint --fix src --ext .ts,.tsx" + }, + "files": [ + "dist" + ] +} diff --git a/design-tokens/src/colors.ts b/design-tokens/src/colors.ts new file mode 100644 index 00000000..87380aae --- /dev/null +++ b/design-tokens/src/colors.ts @@ -0,0 +1,129 @@ +// Copyright 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 type HexColor = `#${string}`; + +export interface PersesColor { + 50: HexColor; + 100: HexColor; + 150: HexColor; + 200: HexColor; + 300: HexColor; + 400: HexColor; + 500: HexColor; + 600: HexColor; + 700: HexColor; + 800: HexColor; + 850: HexColor; + 900: HexColor; + 950: HexColor; +} + +export const blue: PersesColor = { + 50: '#E7F1FC', + 100: '#D0E3FA', + 150: '#B8D5F7', + 200: '#A1C7F5', + 300: '#72ABF0', + 400: '#438FEB', + 500: '#1473E6', + 600: '#105CB8', + 700: '#0C458A', + 800: '#082E5C', + 850: '#062345', + 900: '#04172E', + 950: '#020C17', +}; + +export const green: PersesColor = { + 50: '#EAF9F1', + 100: '#D5F2E3', + 150: '#C1ECD4', + 200: '#ACE5C6', + 300: '#82D9AA', + 400: '#59CC8D', + 500: '#2FBF71', + 600: '#26995A', + 700: '#1C7344', + 800: '#134C2D', + 850: '#0E3922', + 900: '#092617', + 950: '#05130B', +}; + +export const gray: PersesColor = { + 50: '#F0F1F6', + 100: '#E1E3ED', + 150: '#D2D5E4', + 200: '#C3C7DB', + 300: '#A4ACC8', + 400: '#8690B6', + 500: '#717CA4', + 600: '#535D83', + 700: '#3E4662', + 800: '#2A2E42', + 850: '#1F2331', + 900: '#151721', + 950: '#0A0C10', +}; + +export const orange: PersesColor = { + 50: '#FFF5E8', + 100: '#FFECD2', + 150: '#FFE2BB', + 200: '#FFD9A4', + 300: '#FFC577', + 400: '#FFB249', + 500: '#FF9F1C', + 600: '#CC7F16', + 700: '#995F11', + 800: '#66400B', + 850: '#4D3008', + 900: '#332006', + 950: '#1A1003', +}; + +export const purple: PersesColor = { + 50: '#EFE9FD', + 100: '#E0D2FC', + 150: '#D0BCFA', + 200: '#C1A6F8', + 300: '#A179F5', + 400: '#824DF1', + 500: '#6320EE', + 600: '#4F1ABE', + 700: '#3B138F', + 800: '#280D5F', + 850: '#1E0A47', + 900: '#140630', + 950: '#0A0318', +}; + +export const red: PersesColor = { + 50: '#FDEDED', + 100: '#FBDADA', + 150: '#F9C8C8', + 200: '#F7B5B5', + 300: '#F29191', + 400: '#EE6C6C', + 500: '#EA4747', + 600: '#BD3939', + 700: '#902B2B', + 800: '#621D1D', + 850: '#4C1616', + 900: '#350F0F', + 950: '#1F0808', +}; + +export const white = '#FFFFFF' as HexColor; +export const black = '#000000' as HexColor; diff --git a/design-tokens/src/css/index.css b/design-tokens/src/css/index.css new file mode 100644 index 00000000..8749d048 --- /dev/null +++ b/design-tokens/src/css/index.css @@ -0,0 +1,20 @@ +/* + * Copyright 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. + */ + +@layer perses.reset, perses.tokens, perses.semantic; + +@import './reset.css'; +@import './tokens.css'; +@import './semantic.css'; diff --git a/design-tokens/src/css/reset.css b/design-tokens/src/css/reset.css new file mode 100644 index 00000000..d211b250 --- /dev/null +++ b/design-tokens/src/css/reset.css @@ -0,0 +1,28 @@ +/* + * Copyright 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. + */ + +@layer perses.reset { + *, + *::before, + *::after { + box-sizing: border-box; + } + + body { + margin: 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } +} diff --git a/design-tokens/src/css/semantic.css b/design-tokens/src/css/semantic.css new file mode 100644 index 00000000..2f4d37c6 --- /dev/null +++ b/design-tokens/src/css/semantic.css @@ -0,0 +1,162 @@ +/* + * Copyright 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. + */ + +@layer perses.semantic { + /* + * Elevation hierarchy (lowest → highest): + * bg-default — page/app background (base level) + * bg-surface — raised surfaces: cards, panels, dialogs + * bg-sunken — recessed areas: code blocks, inset regions + * bg-overlay — floating surfaces: tooltips, popovers, dropdowns + * bg-backdrop — semi-transparent scrim behind modals + * + * Other backgrounds: + * bg-navigation — sidebar/nav area (brand-tinted) + * + * Border: + * border-default — general-purpose border color + */ + + /* ---- Light mode (default) ---- */ + :root { + /* Background: Elevation */ + --perses-bg-default: var(--perses-color-white); + --perses-bg-surface: var(--perses-color-gray-50); + --perses-bg-sunken: var(--perses-color-gray-200); + --perses-bg-overlay: var(--perses-color-gray-100); + --perses-bg-backdrop: rgba(21, 23, 33, 0.75); + --perses-bg-navigation: var(--perses-color-blue-150); + + /* Border */ + --perses-border-default: var(--perses-color-gray-100); + + /* Text */ + --perses-text-primary: var(--perses-color-gray-800); + --perses-text-secondary: var(--perses-color-gray-700); + --perses-text-disabled: var(--perses-color-gray-300); + --perses-text-link: var(--perses-color-blue-500); + --perses-text-link-hover: var(--perses-color-blue-600); + --perses-text-navigation: var(--perses-color-gray-800); + --perses-text-accent: var(--perses-color-gray-300); + + /* Status: Primary */ + --perses-status-bg-primary: var(--perses-color-blue-50); + --perses-status-bg-primary-hover: var(--perses-color-blue-100); + --perses-status-text-primary: var(--perses-color-blue-700); + --perses-status-border-primary: var(--perses-color-blue-200); + --perses-status-icon-primary: var(--perses-color-blue-600); + + /* Status: Secondary */ + --perses-status-bg-secondary: var(--perses-color-gray-50); + --perses-status-bg-secondary-hover: var(--perses-color-gray-100); + --perses-status-text-secondary: var(--perses-color-gray-700); + --perses-status-border-secondary: var(--perses-color-gray-200); + --perses-status-icon-secondary: var(--perses-color-gray-600); + + /* Status: Error */ + --perses-status-bg-error: var(--perses-color-red-50); + --perses-status-bg-error-hover: var(--perses-color-red-100); + --perses-status-text-error: var(--perses-color-red-700); + --perses-status-border-error: var(--perses-color-red-200); + --perses-status-icon-error: var(--perses-color-red-600); + + /* Status: Warning */ + --perses-status-bg-warning: var(--perses-color-orange-50); + --perses-status-bg-warning-hover: var(--perses-color-orange-100); + --perses-status-text-warning: var(--perses-color-orange-700); + --perses-status-border-warning: var(--perses-color-orange-200); + --perses-status-icon-warning: var(--perses-color-orange-600); + + /* Status: Success */ + --perses-status-bg-success: var(--perses-color-green-50); + --perses-status-bg-success-hover: var(--perses-color-green-100); + --perses-status-text-success: var(--perses-color-green-700); + --perses-status-border-success: var(--perses-color-green-200); + --perses-status-icon-success: var(--perses-color-green-600); + + /* Status: Info */ + --perses-status-bg-info: var(--perses-color-blue-50); + --perses-status-bg-info-hover: var(--perses-color-blue-100); + --perses-status-text-info: var(--perses-color-blue-700); + --perses-status-border-info: var(--perses-color-blue-200); + --perses-status-icon-info: var(--perses-color-blue-600); + } + + /* ---- Dark mode: explicit (data attribute) ---- */ + [data-perses-mode='dark'] { + /* Background: Elevation */ + --perses-bg-default: var(--perses-color-gray-900); + --perses-bg-surface: var(--perses-color-gray-850); + --perses-bg-sunken: var(--perses-color-gray-800); + --perses-bg-overlay: var(--perses-color-gray-600); + --perses-bg-backdrop: rgba(10, 12, 16, 0.85); + --perses-bg-navigation: var(--perses-color-gray-850); + + /* Border */ + --perses-border-default: var(--perses-color-gray-600); + + /* Text */ + --perses-text-primary: var(--perses-color-white); + --perses-text-secondary: var(--perses-color-gray-50); + --perses-text-disabled: var(--perses-color-gray-600); + --perses-text-link: var(--perses-color-blue-400); + --perses-text-link-hover: var(--perses-color-blue-500); + --perses-text-navigation: var(--perses-color-white); + --perses-text-accent: var(--perses-color-gray-400); + + /* Status: Primary */ + --perses-status-bg-primary: var(--perses-color-blue-900); + --perses-status-bg-primary-hover: var(--perses-color-blue-850); + --perses-status-text-primary: var(--perses-color-blue-300); + --perses-status-border-primary: var(--perses-color-blue-700); + --perses-status-icon-primary: var(--perses-color-blue-400); + + /* Status: Secondary */ + --perses-status-bg-secondary: var(--perses-color-gray-850); + --perses-status-bg-secondary-hover: var(--perses-color-gray-800); + --perses-status-text-secondary: var(--perses-color-gray-200); + --perses-status-border-secondary: var(--perses-color-gray-700); + --perses-status-icon-secondary: var(--perses-color-gray-400); + + /* Status: Error */ + --perses-status-bg-error: var(--perses-color-red-900); + --perses-status-bg-error-hover: var(--perses-color-red-850); + --perses-status-text-error: var(--perses-color-red-300); + --perses-status-border-error: var(--perses-color-red-700); + --perses-status-icon-error: var(--perses-color-red-400); + + /* Status: Warning */ + --perses-status-bg-warning: var(--perses-color-orange-900); + --perses-status-bg-warning-hover: var(--perses-color-orange-850); + --perses-status-text-warning: var(--perses-color-orange-300); + --perses-status-border-warning: var(--perses-color-orange-700); + --perses-status-icon-warning: var(--perses-color-orange-400); + + /* Status: Success */ + --perses-status-bg-success: var(--perses-color-green-900); + --perses-status-bg-success-hover: var(--perses-color-green-850); + --perses-status-text-success: var(--perses-color-green-300); + --perses-status-border-success: var(--perses-color-green-700); + --perses-status-icon-success: var(--perses-color-green-400); + + /* Status: Info */ + --perses-status-bg-info: var(--perses-color-blue-900); + --perses-status-bg-info-hover: var(--perses-color-blue-850); + --perses-status-text-info: var(--perses-color-blue-300); + --perses-status-border-info: var(--perses-color-blue-700); + --perses-status-icon-info: var(--perses-color-blue-400); + } + +} diff --git a/design-tokens/src/css/tokens.css b/design-tokens/src/css/tokens.css new file mode 100644 index 00000000..246b3279 --- /dev/null +++ b/design-tokens/src/css/tokens.css @@ -0,0 +1,155 @@ +/* + * Copyright 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. + */ + +@layer perses.tokens { + :root { + /* ---- Colors: Blue ---- */ + --perses-color-blue-50: #e7f1fc; + --perses-color-blue-100: #d0e3fa; + --perses-color-blue-150: #b8d5f7; + --perses-color-blue-200: #a1c7f5; + --perses-color-blue-300: #72abf0; + --perses-color-blue-400: #438feb; + --perses-color-blue-500: #1473e6; + --perses-color-blue-600: #105cb8; + --perses-color-blue-700: #0c458a; + --perses-color-blue-800: #082e5c; + --perses-color-blue-850: #062345; + --perses-color-blue-900: #04172e; + --perses-color-blue-950: #020c17; + + /* ---- Colors: Green ---- */ + --perses-color-green-50: #eaf9f1; + --perses-color-green-100: #d5f2e3; + --perses-color-green-150: #c1ecd4; + --perses-color-green-200: #ace5c6; + --perses-color-green-300: #82d9aa; + --perses-color-green-400: #59cc8d; + --perses-color-green-500: #2fbf71; + --perses-color-green-600: #26995a; + --perses-color-green-700: #1c7344; + --perses-color-green-800: #134c2d; + --perses-color-green-850: #0e3922; + --perses-color-green-900: #092617; + --perses-color-green-950: #05130b; + + /* ---- Colors: Gray ---- */ + --perses-color-gray-50: #f0f1f6; + --perses-color-gray-100: #e1e3ed; + --perses-color-gray-150: #d2d5e4; + --perses-color-gray-200: #c3c7db; + --perses-color-gray-300: #a4acc8; + --perses-color-gray-400: #8690b6; + --perses-color-gray-500: #717ca4; + --perses-color-gray-600: #535d83; + --perses-color-gray-700: #3e4662; + --perses-color-gray-800: #2a2e42; + --perses-color-gray-850: #1f2331; + --perses-color-gray-900: #151721; + --perses-color-gray-950: #0a0c10; + + /* ---- Colors: Orange ---- */ + --perses-color-orange-50: #fff5e8; + --perses-color-orange-100: #ffecd2; + --perses-color-orange-150: #ffe2bb; + --perses-color-orange-200: #ffd9a4; + --perses-color-orange-300: #ffc577; + --perses-color-orange-400: #ffb249; + --perses-color-orange-500: #ff9f1c; + --perses-color-orange-600: #cc7f16; + --perses-color-orange-700: #995f11; + --perses-color-orange-800: #66400b; + --perses-color-orange-850: #4d3008; + --perses-color-orange-900: #332006; + --perses-color-orange-950: #1a1003; + + /* ---- Colors: Purple ---- */ + --perses-color-purple-50: #efe9fd; + --perses-color-purple-100: #e0d2fc; + --perses-color-purple-150: #d0bcfa; + --perses-color-purple-200: #c1a6f8; + --perses-color-purple-300: #a179f5; + --perses-color-purple-400: #824df1; + --perses-color-purple-500: #6320ee; + --perses-color-purple-600: #4f1abe; + --perses-color-purple-700: #3b138f; + --perses-color-purple-800: #280d5f; + --perses-color-purple-850: #1e0a47; + --perses-color-purple-900: #140630; + --perses-color-purple-950: #0a0318; + + /* ---- Colors: Red ---- */ + --perses-color-red-50: #fdeded; + --perses-color-red-100: #fbdada; + --perses-color-red-150: #f9c8c8; + --perses-color-red-200: #f7b5b5; + --perses-color-red-300: #f29191; + --perses-color-red-400: #ee6c6c; + --perses-color-red-500: #ea4747; + --perses-color-red-600: #bd3939; + --perses-color-red-700: #902b2b; + --perses-color-red-800: #621d1d; + --perses-color-red-850: #4c1616; + --perses-color-red-900: #350f0f; + --perses-color-red-950: #1f0808; + + /* ---- Colors: Common ---- */ + --perses-color-white: #ffffff; + --perses-color-black: #000000; + + /* ---- Spacing (rem) ---- */ + --perses-spacing-0: 0; + --perses-spacing-xs: 0.25rem; + --perses-spacing-sm: 0.5rem; + --perses-spacing-md: 0.75rem; + --perses-spacing-lg: 1rem; + --perses-spacing-xl: 1.25rem; + --perses-spacing-2xl: 1.5rem; + --perses-spacing-3xl: 2rem; + --perses-spacing-4xl: 3rem; + + /* ---- Border Radius ---- */ + --perses-radius-none: 0; + --perses-radius-sm: 2px; + --perses-radius-md: 4px; + --perses-radius-lg: 8px; + --perses-radius-xl: 12px; + --perses-radius-full: 9999px; + + /* ---- Typography ---- */ + --perses-font-family: + Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; + + --perses-font-weight-light: 300; + --perses-font-weight-regular: 400; + --perses-font-weight-medium: 600; + --perses-font-weight-bold: 700; + + --perses-font-size-xs: 0.75rem; + --perses-font-size-sm: 0.875rem; + --perses-font-size-md: 1rem; + --perses-font-size-lg: 1.25rem; + --perses-font-size-xl: 1.5rem; + --perses-font-size-2xl: 2rem; + --perses-font-size-3xl: 2.5rem; + --perses-font-size-4xl: 3rem; + + --perses-line-height-tight: 1.2; + --perses-line-height-compact: 1.3; + --perses-line-height-normal: 1.4; + --perses-line-height-relaxed: 1.5; + } +} diff --git a/design-tokens/src/index.ts b/design-tokens/src/index.ts new file mode 100644 index 00000000..d627d721 --- /dev/null +++ b/design-tokens/src/index.ts @@ -0,0 +1,16 @@ +// Copyright 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 './types'; +export { tokens } from './tokens'; +export { blue, green, gray, orange, purple, red, white, black } from './colors'; diff --git a/design-tokens/src/test/consistency.test.ts b/design-tokens/src/test/consistency.test.ts new file mode 100644 index 00000000..2204fb85 --- /dev/null +++ b/design-tokens/src/test/consistency.test.ts @@ -0,0 +1,60 @@ +// Copyright 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 { tokens } from '../tokens'; + +const cssDir = resolve(__dirname, '../css'); +const readCss = (f: string): string => readFileSync(resolve(cssDir, f), 'utf-8'); + +function extractCssVarDefinitions(...files: string[]): Set { + const vars = new Set(); + for (const file of files) { + const css = readCss(file); + for (const match of css.matchAll(/^\s*(--perses-[\w-]+)\s*:/gm)) { + vars.add(match[1]!); + } + } + return vars; +} + +function extractTokenVarRefs(obj: Record): Set { + const vars = new Set(); + for (const value of Object.values(obj)) { + if (typeof value === 'string') { + const match = value.match(/^var\((--perses-[\w-]+)\)$/); + if (match) vars.add(match[1]!); + } else if (typeof value === 'object' && value !== null) { + for (const v of extractTokenVarRefs(value as Record)) { + vars.add(v); + } + } + } + return vars; +} + +describe('token ↔ CSS consistency', () => { + const cssVars = extractCssVarDefinitions('tokens.css', 'semantic.css'); + const tokenVars = extractTokenVarRefs(tokens as unknown as Record); + + it('every CSS variable has a corresponding tokens entry', () => { + const missing = [...cssVars].filter((v) => !tokenVars.has(v)).sort(); + expect(missing).toEqual([]); + }); + + it('every tokens entry references a defined CSS variable', () => { + const missing = [...tokenVars].filter((v) => !cssVars.has(v)).sort(); + expect(missing).toEqual([]); + }); +}); diff --git a/design-tokens/src/test/css.test.ts b/design-tokens/src/test/css.test.ts new file mode 100644 index 00000000..c23f0b54 --- /dev/null +++ b/design-tokens/src/test/css.test.ts @@ -0,0 +1,115 @@ +// Copyright 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 { blue, green, gray, orange, purple, red, white, black, type PersesColor } from '../colors'; + +const cssDir = resolve(__dirname, '../css'); + +const readCss = (filename: string): string => readFileSync(resolve(cssDir, filename), 'utf-8'); + +describe('CSS layer declarations', () => { + it('index.css declares layer order', () => { + const css = readCss('index.css'); + expect(css).toContain('@layer perses.reset, perses.tokens, perses.semantic'); + }); + + it('reset.css uses @layer perses.reset', () => { + const css = readCss('reset.css'); + expect(css).toContain('@layer perses.reset'); + }); + + it('tokens.css uses @layer perses.tokens', () => { + const css = readCss('tokens.css'); + expect(css).toContain('@layer perses.tokens'); + }); + + it('semantic.css uses @layer perses.semantic', () => { + const css = readCss('semantic.css'); + expect(css).toContain('@layer perses.semantic'); + }); +}); + +describe('CSS primitive color variables', () => { + const tokensCss = readCss('tokens.css'); + const tokensCssUpper = tokensCss.toUpperCase(); + + const hues: Array<[string, PersesColor]> = [ + ['blue', blue], + ['green', green], + ['gray', gray], + ['orange', orange], + ['purple', purple], + ['red', red], + ]; + + const stops = [50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 850, 900, 950] as const; + + it.each(hues)('tokens.css defines all %s color variables', (hue, colorObj) => { + for (const stop of stops) { + expect(tokensCss).toContain(`--perses-color-${hue}-${stop}`); + expect(tokensCssUpper).toContain(colorObj[stop].toUpperCase()); + } + }); + + it('tokens.css defines white and black', () => { + expect(tokensCss).toContain('--perses-color-white'); + expect(tokensCss).toContain('--perses-color-black'); + expect(tokensCssUpper).toContain(white.toUpperCase()); + expect(tokensCssUpper).toContain(black.toUpperCase()); + }); +}); + +describe('CSS semantic variables', () => { + const semanticCss = readCss('semantic.css'); + + it('defines light mode defaults on :root', () => { + expect(semanticCss).toContain(':root {'); + expect(semanticCss).toContain('--perses-bg-default'); + expect(semanticCss).toContain('--perses-text-primary'); + }); + + it('defines dark mode via data attribute', () => { + expect(semanticCss).toContain(`[data-perses-mode='dark']`); + }); + + it('has all background semantic tokens', () => { + const bgTokens = ['default', 'surface', 'sunken', 'overlay', 'backdrop', 'navigation']; + for (const name of bgTokens) { + expect(semanticCss).toContain(`--perses-bg-${name}`); + } + }); + + it('has border semantic token', () => { + expect(semanticCss).toContain('--perses-border-default'); + }); + + it('has all text semantic tokens', () => { + const textTokens = ['primary', 'secondary', 'disabled', 'link', 'link-hover', 'navigation', 'accent']; + for (const name of textTokens) { + expect(semanticCss).toContain(`--perses-text-${name}`); + } + }); + + it('has all status tokens with property-scoped naming', () => { + const roles = ['primary', 'secondary', 'error', 'warning', 'success', 'info']; + const properties = ['bg', 'text', 'border', 'icon']; + for (const role of roles) { + for (const prop of properties) { + expect(semanticCss).toContain(`--perses-status-${prop}-${role}`); + } + expect(semanticCss).toContain(`--perses-status-bg-${role}-hover`); + } + }); +}); diff --git a/design-tokens/src/test/tokens.test.ts b/design-tokens/src/test/tokens.test.ts new file mode 100644 index 00000000..4caaef09 --- /dev/null +++ b/design-tokens/src/test/tokens.test.ts @@ -0,0 +1,132 @@ +// Copyright 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 { tokens } from '../tokens'; +import { blue, green, gray, orange, purple, red, white, black } from '../colors'; + +const HEX_PATTERN = /^#[0-9A-Fa-f]{6}$/; + +describe('color constants', () => { + const colorEntries = [ + ['blue', blue], + ['green', green], + ['gray', gray], + ['orange', orange], + ['purple', purple], + ['red', red], + ] as const; + + it.each(colorEntries)('%s has valid hex values for all stops', (_name, color) => { + const stops = [50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 850, 900, 950] as const; + for (const stop of stops) { + expect(color[stop]).toMatch(HEX_PATTERN); + } + }); + + it('white and black are valid hex', () => { + expect(white).toMatch(HEX_PATTERN); + expect(black).toMatch(HEX_PATTERN); + }); +}); + +describe('tokens object', () => { + it('produces correct var() strings for primitive colors', () => { + expect(tokens.color.blue[500]).toBe('var(--perses-color-blue-500)'); + expect(tokens.color.gray[100]).toBe('var(--perses-color-gray-100)'); + expect(tokens.color.red[50]).toBe('var(--perses-color-red-50)'); + expect(tokens.color.white).toBe('var(--perses-color-white)'); + expect(tokens.color.black).toBe('var(--perses-color-black)'); + }); + + it('produces correct var() strings for semantic background tokens', () => { + expect(tokens.bg.default).toBe('var(--perses-bg-default)'); + expect(tokens.bg.surface).toBe('var(--perses-bg-surface)'); + expect(tokens.bg.sunken).toBe('var(--perses-bg-sunken)'); + expect(tokens.bg.overlay).toBe('var(--perses-bg-overlay)'); + expect(tokens.bg.backdrop).toBe('var(--perses-bg-backdrop)'); + expect(tokens.bg.navigation).toBe('var(--perses-bg-navigation)'); + }); + + it('produces correct var() strings for semantic border tokens', () => { + expect(tokens.border.default).toBe('var(--perses-border-default)'); + }); + + it('produces correct var() strings for semantic text tokens', () => { + expect(tokens.text.primary).toBe('var(--perses-text-primary)'); + expect(tokens.text.link).toBe('var(--perses-text-link)'); + expect(tokens.text.disabled).toBe('var(--perses-text-disabled)'); + }); + + it('produces correct var() strings for status tokens', () => { + expect(tokens.status.success.bg).toBe('var(--perses-status-bg-success)'); + expect(tokens.status.success.bgHover).toBe('var(--perses-status-bg-success-hover)'); + expect(tokens.status.success.text).toBe('var(--perses-status-text-success)'); + expect(tokens.status.success.border).toBe('var(--perses-status-border-success)'); + expect(tokens.status.success.icon).toBe('var(--perses-status-icon-success)'); + + expect(tokens.status.error.bg).toBe('var(--perses-status-bg-error)'); + expect(tokens.status.error.text).toBe('var(--perses-status-text-error)'); + expect(tokens.status.warning.border).toBe('var(--perses-status-border-warning)'); + expect(tokens.status.info.icon).toBe('var(--perses-status-icon-info)'); + expect(tokens.status.primary.bg).toBe('var(--perses-status-bg-primary)'); + expect(tokens.status.secondary.bgHover).toBe('var(--perses-status-bg-secondary-hover)'); + }); + + it('has all 6 status roles with 5 properties each', () => { + const roles = ['primary', 'secondary', 'error', 'warning', 'success', 'info'] as const; + const properties = ['bg', 'bgHover', 'text', 'border', 'icon'] as const; + for (const role of roles) { + for (const prop of properties) { + expect(tokens.status[role][prop]).toBeDefined(); + expect(tokens.status[role][prop]).toMatch(/^var\(--perses-status-/); + } + } + }); + + it('produces correct var() strings for spacing tokens', () => { + expect(tokens.spacing[0]).toBe('var(--perses-spacing-0)'); + expect(tokens.spacing.xs).toBe('var(--perses-spacing-xs)'); + expect(tokens.spacing.sm).toBe('var(--perses-spacing-sm)'); + expect(tokens.spacing.md).toBe('var(--perses-spacing-md)'); + expect(tokens.spacing.lg).toBe('var(--perses-spacing-lg)'); + expect(tokens.spacing.xl).toBe('var(--perses-spacing-xl)'); + expect(tokens.spacing['2xl']).toBe('var(--perses-spacing-2xl)'); + expect(tokens.spacing['3xl']).toBe('var(--perses-spacing-3xl)'); + expect(tokens.spacing['4xl']).toBe('var(--perses-spacing-4xl)'); + }); + + it('produces correct var() strings for radius tokens', () => { + expect(tokens.radius.none).toBe('var(--perses-radius-none)'); + expect(tokens.radius.md).toBe('var(--perses-radius-md)'); + expect(tokens.radius.full).toBe('var(--perses-radius-full)'); + }); + + it('produces correct var() strings for typography tokens', () => { + expect(tokens.font.family).toBe('var(--perses-font-family)'); + expect(tokens.font.weight.bold).toBe('var(--perses-font-weight-bold)'); + expect(tokens.font.size.xs).toBe('var(--perses-font-size-xs)'); + expect(tokens.font.size.sm).toBe('var(--perses-font-size-sm)'); + expect(tokens.font.size.md).toBe('var(--perses-font-size-md)'); + expect(tokens.font.size['2xl']).toBe('var(--perses-font-size-2xl)'); + expect(tokens.font.lineHeight.tight).toBe('var(--perses-line-height-tight)'); + expect(tokens.font.lineHeight.compact).toBe('var(--perses-line-height-compact)'); + expect(tokens.font.lineHeight.normal).toBe('var(--perses-line-height-normal)'); + expect(tokens.font.lineHeight.relaxed).toBe('var(--perses-line-height-relaxed)'); + }); + + it('has all expected top-level categories', () => { + expect(Object.keys(tokens)).toEqual( + expect.arrayContaining(['color', 'bg', 'border', 'text', 'status', 'spacing', 'radius', 'font']) + ); + }); +}); diff --git a/design-tokens/src/test/type-assertions.ts b/design-tokens/src/test/type-assertions.ts new file mode 100644 index 00000000..75ab9ff3 --- /dev/null +++ b/design-tokens/src/test/type-assertions.ts @@ -0,0 +1,62 @@ +// Copyright 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. + +// Compile-time type assertions: if a token var name is missing from +// PersesTokenVar, `npm run type-check` will fail here. +// This file is never executed — it only needs to pass tsc. + +import type { PersesTokenVar } from '../types'; +import { tokens } from '../tokens'; + +type ExtractVar = T extends `var(${infer V})` ? V : never; + +type AssertAssignable = T; + +// Verify that the var() references in the tokens object produce variable names +// that are assignable to PersesTokenVar. If a token references a CSS variable +// not covered by PersesTokenVar, tsc will emit an error on this line. +type _BgVars = AssertAssignable, PersesTokenVar>; +type _BgSurface = AssertAssignable, PersesTokenVar>; +type _BgSunken = AssertAssignable, PersesTokenVar>; +type _BgOverlay = AssertAssignable, PersesTokenVar>; +type _BgBackdrop = AssertAssignable, PersesTokenVar>; +type _BgNav = AssertAssignable, PersesTokenVar>; + +type _BorderDefault = AssertAssignable, PersesTokenVar>; + +type _TextPrimary = AssertAssignable, PersesTokenVar>; +type _TextSecondary = AssertAssignable, PersesTokenVar>; +type _TextDisabled = AssertAssignable, PersesTokenVar>; +type _TextLink = AssertAssignable, PersesTokenVar>; +type _TextLinkHover = AssertAssignable, PersesTokenVar>; +type _TextNav = AssertAssignable, PersesTokenVar>; +type _TextAccent = AssertAssignable, PersesTokenVar>; + +type _SpacingXs = AssertAssignable, PersesTokenVar>; +type _SpacingLg = AssertAssignable, PersesTokenVar>; + +type _RadiusMd = AssertAssignable, PersesTokenVar>; +type _RadiusFull = AssertAssignable, PersesTokenVar>; + +type _FontFamily = AssertAssignable, PersesTokenVar>; +type _FontWeightBold = AssertAssignable, PersesTokenVar>; +type _FontSizeSm = AssertAssignable, PersesTokenVar>; +type _LineHeightTight = AssertAssignable, PersesTokenVar>; + +type _StatusErrorBg = AssertAssignable, PersesTokenVar>; +type _StatusSuccessText = AssertAssignable, PersesTokenVar>; +type _StatusWarningBorder = AssertAssignable, PersesTokenVar>; +type _StatusInfoIcon = AssertAssignable, PersesTokenVar>; + +type _ColorBlue500 = AssertAssignable, PersesTokenVar>; +type _ColorWhite = AssertAssignable, PersesTokenVar>; diff --git a/design-tokens/src/tokens.ts b/design-tokens/src/tokens.ts new file mode 100644 index 00000000..1f68128a --- /dev/null +++ b/design-tokens/src/tokens.ts @@ -0,0 +1,132 @@ +// Copyright 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 { ColorHue, StatusRole } from './types'; + +const colorScale = (hue: ColorHue) => + ({ + 50: `var(--perses-color-${hue}-50)`, + 100: `var(--perses-color-${hue}-100)`, + 150: `var(--perses-color-${hue}-150)`, + 200: `var(--perses-color-${hue}-200)`, + 300: `var(--perses-color-${hue}-300)`, + 400: `var(--perses-color-${hue}-400)`, + 500: `var(--perses-color-${hue}-500)`, + 600: `var(--perses-color-${hue}-600)`, + 700: `var(--perses-color-${hue}-700)`, + 800: `var(--perses-color-${hue}-800)`, + 850: `var(--perses-color-${hue}-850)`, + 900: `var(--perses-color-${hue}-900)`, + 950: `var(--perses-color-${hue}-950)`, + }) as const; + +const statusRole = (role: StatusRole) => + ({ + bg: `var(--perses-status-bg-${role})`, + bgHover: `var(--perses-status-bg-${role}-hover)`, + text: `var(--perses-status-text-${role})`, + border: `var(--perses-status-border-${role})`, + icon: `var(--perses-status-icon-${role})`, + }) as const; + +export const tokens = { + color: { + blue: colorScale('blue'), + green: colorScale('green'), + gray: colorScale('gray'), + orange: colorScale('orange'), + purple: colorScale('purple'), + red: colorScale('red'), + white: 'var(--perses-color-white)', + black: 'var(--perses-color-black)', + }, + + bg: { + default: 'var(--perses-bg-default)', + surface: 'var(--perses-bg-surface)', + sunken: 'var(--perses-bg-sunken)', + overlay: 'var(--perses-bg-overlay)', + backdrop: 'var(--perses-bg-backdrop)', + navigation: 'var(--perses-bg-navigation)', + }, + + border: { + default: 'var(--perses-border-default)', + }, + + text: { + primary: 'var(--perses-text-primary)', + secondary: 'var(--perses-text-secondary)', + disabled: 'var(--perses-text-disabled)', + link: 'var(--perses-text-link)', + linkHover: 'var(--perses-text-link-hover)', + navigation: 'var(--perses-text-navigation)', + accent: 'var(--perses-text-accent)', + }, + + status: { + primary: statusRole('primary'), + secondary: statusRole('secondary'), + error: statusRole('error'), + warning: statusRole('warning'), + success: statusRole('success'), + info: statusRole('info'), + }, + + spacing: { + '0': 'var(--perses-spacing-0)', + xs: 'var(--perses-spacing-xs)', + sm: 'var(--perses-spacing-sm)', + md: 'var(--perses-spacing-md)', + lg: 'var(--perses-spacing-lg)', + xl: 'var(--perses-spacing-xl)', + '2xl': 'var(--perses-spacing-2xl)', + '3xl': 'var(--perses-spacing-3xl)', + '4xl': 'var(--perses-spacing-4xl)', + }, + + radius: { + none: 'var(--perses-radius-none)', + sm: 'var(--perses-radius-sm)', + md: 'var(--perses-radius-md)', + lg: 'var(--perses-radius-lg)', + xl: 'var(--perses-radius-xl)', + full: 'var(--perses-radius-full)', + }, + + font: { + family: 'var(--perses-font-family)', + weight: { + light: 'var(--perses-font-weight-light)', + regular: 'var(--perses-font-weight-regular)', + medium: 'var(--perses-font-weight-medium)', + bold: 'var(--perses-font-weight-bold)', + }, + size: { + xs: 'var(--perses-font-size-xs)', + sm: 'var(--perses-font-size-sm)', + md: 'var(--perses-font-size-md)', + lg: 'var(--perses-font-size-lg)', + xl: 'var(--perses-font-size-xl)', + '2xl': 'var(--perses-font-size-2xl)', + '3xl': 'var(--perses-font-size-3xl)', + '4xl': 'var(--perses-font-size-4xl)', + }, + lineHeight: { + tight: 'var(--perses-line-height-tight)', + compact: 'var(--perses-line-height-compact)', + normal: 'var(--perses-line-height-normal)', + relaxed: 'var(--perses-line-height-relaxed)', + }, + }, +} as const; diff --git a/design-tokens/src/types.ts b/design-tokens/src/types.ts new file mode 100644 index 00000000..6e633945 --- /dev/null +++ b/design-tokens/src/types.ts @@ -0,0 +1,87 @@ +// Copyright 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 type { HexColor, PersesColor } from './colors'; + +export type ColorStop = 50 | 100 | 150 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 850 | 900 | 950; + +export type ColorHue = 'blue' | 'green' | 'gray' | 'orange' | 'purple' | 'red'; + +export type PrimitiveColorVar = `--perses-color-${ColorHue}-${ColorStop}`; + +export type CommonColorVar = '--perses-color-white' | '--perses-color-black'; + +export type SemanticBgVar = + | '--perses-bg-default' + | '--perses-bg-surface' + | '--perses-bg-sunken' + | '--perses-bg-overlay' + | '--perses-bg-backdrop' + | '--perses-bg-navigation'; + +export type SemanticBorderVar = '--perses-border-default'; + +export type SemanticTextVar = + | '--perses-text-primary' + | '--perses-text-secondary' + | '--perses-text-disabled' + | '--perses-text-link' + | '--perses-text-link-hover' + | '--perses-text-navigation' + | '--perses-text-accent'; + +export type StatusRole = 'primary' | 'secondary' | 'error' | 'warning' | 'success' | 'info'; + +export type StatusBgVar = `--perses-status-bg-${StatusRole}` | `--perses-status-bg-${StatusRole}-hover`; + +export type StatusTextVar = `--perses-status-text-${StatusRole}`; + +export type StatusBorderVar = `--perses-status-border-${StatusRole}`; + +export type StatusIconVar = `--perses-status-icon-${StatusRole}`; + +export type SpacingScale = '0' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; + +export type SpacingVar = `--perses-spacing-${SpacingScale}`; + +export type RadiusVar = `--perses-radius-${'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full'}`; + +export type FontSizeScale = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; + +export type FontSizeVar = `--perses-font-size-${FontSizeScale}`; + +export type LineHeightScale = 'tight' | 'compact' | 'normal' | 'relaxed'; + +export type LineHeightVar = `--perses-line-height-${LineHeightScale}`; + +export type FontVar = + | '--perses-font-family' + | `--perses-font-weight-${'light' | 'regular' | 'medium' | 'bold'}` + | FontSizeVar + | LineHeightVar; + +export type PersesTokenVar = + | PrimitiveColorVar + | CommonColorVar + | SemanticBgVar + | SemanticBorderVar + | SemanticTextVar + | StatusBgVar + | StatusTextVar + | StatusBorderVar + | StatusIconVar + | SpacingVar + | RadiusVar + | FontVar; + +export type PersesMode = 'light' | 'dark'; diff --git a/design-tokens/tsconfig.build.json b/design-tokens/tsconfig.build.json new file mode 100644 index 00000000..e477966e --- /dev/null +++ b/design-tokens/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["**/*.stories.*", "**/*.test.*", "**/stories/*", "**/test/*"], + "compilerOptions": { + "emitDeclarationOnly": true, + "declaration": true, + "preserveWatchOutput": true + } +} diff --git a/design-tokens/tsconfig.json b/design-tokens/tsconfig.json new file mode 100644 index 00000000..806aa79a --- /dev/null +++ b/design-tokens/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.base.json", + "include": ["src"], + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + } +} diff --git a/jest.shared.ts b/jest.shared.ts index 3cfbe332..8c6f5e48 100644 --- a/jest.shared.ts +++ b/jest.shared.ts @@ -29,7 +29,7 @@ const config: Config.InitialOptions = { '^use-resize-observer$': 'use-resize-observer/polyfilled', // Tell Jest where other Perses packages live since it doesn't know about project references - '^@perses-dev/(client|components|dashboards|explore|plugin-system)(.*)$': '/../$1/src', + '^@perses-dev/(client|components|dashboards|design-tokens|explore|plugin-system)(.*)$': '/../$1/src', // Configure Jest to handle stylesheets '\\.(css|less)$': '/../stylesMock.js', diff --git a/package-lock.json b/package-lock.json index 7030a820..bd30bd80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "workspaces": [ "components", "dashboards", + "design-tokens", "plugin-system", "explore", "client" @@ -177,6 +178,11 @@ "url": "https://github.com/sponsors/eemeli" } }, + "design-tokens": { + "name": "@perses-dev/design-tokens", + "version": "0.54.0-beta.10", + "license": "Apache-2.0" + }, "explore": { "name": "@perses-dev/explore", "version": "0.54.0", @@ -3299,6 +3305,10 @@ "resolved": "dashboards", "link": true }, + "node_modules/@perses-dev/design-tokens": { + "resolved": "design-tokens", + "link": true + }, "node_modules/@perses-dev/explore": { "resolved": "explore", "link": true diff --git a/package.json b/package.json index 7d5add98..cf6598f4 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "workspaces": [ "components", "dashboards", + "design-tokens", "plugin-system", "explore", "client"