diff --git a/.changeset/silly-colts-brush.md b/.changeset/silly-colts-brush.md new file mode 100644 index 0000000000..be1c72ea14 --- /dev/null +++ b/.changeset/silly-colts-brush.md @@ -0,0 +1,8 @@ +--- +"@cloudoperators/juno-app-greenhouse": patch +"@cloudoperators/juno-app-supernova": patch +"@cloudoperators/juno-app-heureka": patch +"@cloudoperators/juno-app-doop": patch +--- + +Makes route search validation Zod v4–ready (filter by prefix before parse) and upgrade zod to v4 diff --git a/apps/carbon/src/components/ErrorBoundary/ErrorFallback.tsx b/apps/carbon/src/components/ErrorBoundary/ErrorFallback.tsx index fdfb985dc5..f5855eebff 100644 --- a/apps/carbon/src/components/ErrorBoundary/ErrorFallback.tsx +++ b/apps/carbon/src/components/ErrorBoundary/ErrorFallback.tsx @@ -8,7 +8,7 @@ import { FallbackProps } from "react-error-boundary" import { Message } from "@cloudoperators/juno-ui-components" const ErrorFallback = ({ error }: FallbackProps) => ( - + ) export default ErrorFallback diff --git a/apps/doop/src/lib/helpers.ts b/apps/doop/src/lib/helpers.ts index 1ef8544c01..6d3a1443f2 100644 --- a/apps/doop/src/lib/helpers.ts +++ b/apps/doop/src/lib/helpers.ts @@ -39,3 +39,30 @@ export function capitalize(string: any) { export const isObjectWithKeys = (value: any) => value !== null && typeof value === "object" && Object.keys(value).length > 0 + +/** + * Filters raw URL search params to only known keys and keys that start with any allowed prefix. + * Use this before Zod parsing when you need prefix-based catchall validation + * (Zod v4 no longer provides ctx.path in preprocess). + * + * Reusable pattern for route validateSearch: filter first, then parse with a schema that has + * .catchall(z.union([z.string(), z.array(z.string()), z.undefined()])). + */ +export function filterSearchParamsByPrefix( + raw: Record, + knownKeys: string[], + allowedPrefixes: string[] +): Record { + const result: Record = {} + for (const key of knownKeys) { + if (Object.prototype.hasOwnProperty.call(raw, key)) { + result[key] = raw[key] + } + } + for (const key of Object.keys(raw)) { + if (allowedPrefixes.some((p) => key.startsWith(p))) { + result[key] = raw[key] + } + } + return result +} diff --git a/apps/doop/src/routes/violations.tsx b/apps/doop/src/routes/violations.tsx index 540f7f0034..217f2251bd 100644 --- a/apps/doop/src/routes/violations.tsx +++ b/apps/doop/src/routes/violations.tsx @@ -17,26 +17,24 @@ import { } from "../components/StoreProvider" import { parseInitialFilters } from "../lib/store/createFiltersSlice" import { isObjectWithKeys } from "../lib/helpers" +import { filterSearchParamsByPrefix } from "../lib/helpers" + +const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined()]) const searchSchema = z .object({ searchTerm: z.string().optional(), violationGroup: z.string().optional(), }) - .catchall( - z.preprocess( - (val, ctx) => { - if (ctx.path.length > 0 && typeof ctx.path[0] === "string" && !ctx.path[0].startsWith(ACTIVE_FILTERS_PREFIX)) { - return undefined - } - return val - }, - z.union([z.string(), z.array(z.string()), z.undefined()]) - ) - ) + .catchall(filterValueSchema) + +function validateViolationsSearch(search: Record): z.infer { + const filtered = filterSearchParamsByPrefix(search, Object.keys(searchSchema.shape), [ACTIVE_FILTERS_PREFIX]) + return searchSchema.parse(filtered) +} export const Route = createFileRoute("/violations")({ - validateSearch: searchSchema, + validateSearch: validateViolationsSearch, beforeLoad: ({ search }) => { // extract alerts specific state from the URL search params const { activeFilters, searchTerm, violationGroup } = convertUrlStateToAppState(search) diff --git a/apps/greenhouse/src/lib/helpers.ts b/apps/greenhouse/src/lib/helpers.ts index 1286aa03f7..e8b5244893 100644 --- a/apps/greenhouse/src/lib/helpers.ts +++ b/apps/greenhouse/src/lib/helpers.ts @@ -18,3 +18,30 @@ export const parseError = (error: any) => { // @ts-expect-error TS(2304): Cannot find name 'errMsg'. return errMsg } + +/** + * Filters raw URL search params to only known keys and keys that start with any allowed prefix. + * Use this before Zod parsing when you need prefix-based catchall validation + * (Zod v4 no longer provides ctx.path in preprocess). + * + * Reusable pattern for route validateSearch: filter first, then parse with a schema that has + * .catchall(z.union([z.string(), z.array(z.string()), z.undefined()])). + */ +export function filterSearchParamsByPrefix( + raw: Record, + knownKeys: string[], + allowedPrefixes: string[] +): Record { + const result: Record = {} + for (const key of knownKeys) { + if (Object.prototype.hasOwnProperty.call(raw, key)) { + result[key] = raw[key] + } + } + for (const key of Object.keys(raw)) { + if (allowedPrefixes.some((p) => key.startsWith(p))) { + result[key] = raw[key] + } + } + return result +} diff --git a/apps/greenhouse/src/routes/admin/plugin-presets/index.tsx b/apps/greenhouse/src/routes/admin/plugin-presets/index.tsx index 65b93580b1..02515551ab 100644 --- a/apps/greenhouse/src/routes/admin/plugin-presets/index.tsx +++ b/apps/greenhouse/src/routes/admin/plugin-presets/index.tsx @@ -8,28 +8,26 @@ import { z } from "zod" import { PluginPresets } from "../../../components/admin/PluginPresets" import { SELECTED_FILTER_PREFIX } from "../../../components/admin/constants" import { extractFilterSettingsFromSearchParams } from "../../../components/admin/utils" +import { filterSearchParamsByPrefix } from "../../../lib/helpers" + +const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined()]) const searchParamsSchema = z .object({ searchTerm: z.string().optional(), }) - .catchall( - z.preprocess( - (val, ctx) => { - if (ctx.path.length > 0 && typeof ctx.path[0] === "string" && !ctx.path[0].startsWith(SELECTED_FILTER_PREFIX)) { - return undefined - } - return val - }, - z.union([z.string(), z.array(z.string()), z.undefined()]) - ) - ) + .catchall(filterValueSchema) export type PluginPresetSearchParams = z.infer +function validatePluginPresetsSearch(search: Record): PluginPresetSearchParams { + const filtered = filterSearchParamsByPrefix(search, Object.keys(searchParamsSchema.shape), [SELECTED_FILTER_PREFIX]) + return searchParamsSchema.parse(filtered) +} + export const Route = createFileRoute("/admin/plugin-presets/")({ component: PluginPresets, - validateSearch: (search: Record) => searchParamsSchema.parse(search), + validateSearch: validatePluginPresetsSearch, loaderDeps: (search) => ({ ...search, }), diff --git a/apps/heureka/src/routes/services/index.tsx b/apps/heureka/src/routes/services/index.tsx index 19f6a8b716..6425a66421 100644 --- a/apps/heureka/src/routes/services/index.tsx +++ b/apps/heureka/src/routes/services/index.tsx @@ -13,29 +13,26 @@ import { SELECTED_FILTER_PREFIX } from "../../constants" import { fetchServices } from "../../api/fetchServices" import { fetchServicesFilters } from "../../api/fetchServicesFilters" import { extractFilterSettingsFromSearchParams } from "../../components/Services/utils" +import { filterSearchParamsByPrefix } from "../../utils" + +const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined()]) -// Schema for validating and transforming search parameters related to /services page. const servicesSearchSchema = z .object({ service: z.string().optional(), searchTerm: z.string().optional(), }) - .catchall( - z.preprocess( - (val, ctx) => { - if (ctx.path.length > 0 && typeof ctx.path[0] === "string" && !ctx.path[0].startsWith(SELECTED_FILTER_PREFIX)) { - return undefined - } - return val - }, - z.union([z.string(), z.array(z.string()), z.undefined()]) - ) - ) + .catchall(filterValueSchema) export type ServicesSearchParams = z.infer +function validateServicesSearch(search: Record): ServicesSearchParams { + const filtered = filterSearchParamsByPrefix(search, Object.keys(servicesSearchSchema.shape), [SELECTED_FILTER_PREFIX]) + return servicesSearchSchema.parse(filtered) as ServicesSearchParams +} + export const Route = createFileRoute("/services/")({ - validateSearch: servicesSearchSchema, + validateSearch: validateServicesSearch, loaderDeps: ({ search }) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { service, ...rest } = search // we're omitting 'service' from the deps so route does not reload when it changes diff --git a/apps/heureka/src/routes/vulnerabilities/index.tsx b/apps/heureka/src/routes/vulnerabilities/index.tsx index 6f29e5f49e..3a2be9f963 100644 --- a/apps/heureka/src/routes/vulnerabilities/index.tsx +++ b/apps/heureka/src/routes/vulnerabilities/index.tsx @@ -10,28 +10,28 @@ import { SELECTED_FILTER_PREFIX } from "../../constants" import { fetchVulnerabilities } from "../../api/fetchVulnerabilities" import { fetchVulnerabilityFilters } from "../../api/fetchVulnerabilityFilters" import { extractFilterSettingsFromSearchParams } from "../../components/Vulnerabilities/utils" +import { filterSearchParamsByPrefix } from "../../utils" + +const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined()]) const vulnerabilitiesSearchSchema = z .object({ vulnerability: z.string().optional(), searchTerm: z.preprocess((val) => (typeof val === "number" ? val.toString() : val), z.string().optional()), }) - .catchall( - z.preprocess( - (val, ctx) => { - if (ctx.path.length > 0 && typeof ctx.path[0] === "string" && !ctx.path[0].startsWith(SELECTED_FILTER_PREFIX)) { - return undefined - } - return val - }, - z.union([z.string(), z.array(z.string()), z.undefined()]) - ) - ) + .catchall(filterValueSchema) export type VulnerabilitiesSearchParams = z.infer +function validateVulnerabilitiesSearch(search: Record): VulnerabilitiesSearchParams { + const filtered = filterSearchParamsByPrefix(search, Object.keys(vulnerabilitiesSearchSchema.shape), [ + SELECTED_FILTER_PREFIX, + ]) + return vulnerabilitiesSearchSchema.parse(filtered) as VulnerabilitiesSearchParams +} + export const Route = createFileRoute("/vulnerabilities/")({ - validateSearch: vulnerabilitiesSearchSchema, + validateSearch: validateVulnerabilitiesSearch, loaderDeps: ({ search }) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { vulnerability, ...rest } = search // we're omitting 'service' from the deps so route does not reload when it changes diff --git a/apps/heureka/src/utils.ts b/apps/heureka/src/utils.ts index 10ae355c9d..5abd956350 100644 --- a/apps/heureka/src/utils.ts +++ b/apps/heureka/src/utils.ts @@ -94,3 +94,30 @@ export const mapObject = (obj: Record, iteratee: (value: T, key }) return result } + +/** + * Filters raw URL search params to only known keys and keys that start with any allowed prefix. + * Use this before Zod parsing when you need prefix-based catchall validation + * (Zod v4 no longer provides ctx.path in preprocess). + * + * Reusable pattern for route validateSearch: filter first, then parse with a schema that has + * .catchall(z.union([z.string(), z.array(z.string()), z.undefined()])). + */ +export function filterSearchParamsByPrefix( + raw: Record, + knownKeys: string[], + allowedPrefixes: string[] +): Record { + const result: Record = {} + for (const key of knownKeys) { + if (Object.prototype.hasOwnProperty.call(raw, key)) { + result[key] = raw[key] + } + } + for (const key of Object.keys(raw)) { + if (allowedPrefixes.some((p) => key.startsWith(p))) { + result[key] = raw[key] + } + } + return result +} diff --git a/apps/supernova/src/lib/urlStateUtils.ts b/apps/supernova/src/lib/urlStateUtils.ts index 30fc04d61d..2ec2056059 100644 --- a/apps/supernova/src/lib/urlStateUtils.ts +++ b/apps/supernova/src/lib/urlStateUtils.ts @@ -50,18 +50,13 @@ export const extractSearchStringFromHashFragment = (searchString: string) => { return searchString.slice(postHashParams, preHashParams === -1 ? undefined : preHashParams) } -export const getFiltersForUrl = (prefix: string, filters: any) => { - return { - ...Object.entries(filters) - .map(([key, value]) => { - if (value === undefined || value === null) return {} - if (Array.isArray(value)) { - return { [`${prefix}${key}`]: value } - } - return { [`${prefix}${key}`]: value } - }) - .reduce((acc, curr) => ({ ...acc, ...curr }), {}), - } +export const getFiltersForUrl = (prefix: string, filters: any): Record => { + return Object.entries(filters ?? {}) + .filter(([, value]) => value !== undefined && value !== null) + .reduce>((acc, [key, value]) => { + acc[`${prefix}${key}`] = Array.isArray(value) ? value : (value as string) + return acc + }, {}) } export const convertAppStateToUrlState = (appState: any) => { diff --git a/apps/supernova/src/lib/utils.ts b/apps/supernova/src/lib/utils.ts index 3a8018f86a..cb9c9e850b 100644 --- a/apps/supernova/src/lib/utils.ts +++ b/apps/supernova/src/lib/utils.ts @@ -140,3 +140,30 @@ export const sortAlerts = (items: any) => { export const isObjectWithKeys = (value: any) => value !== null && typeof value === "object" && Object.keys(value).length > 0 + +/** + * Filters raw URL search params to only known keys and keys that start with any allowed prefix. + * Use this before Zod parsing when you need prefix-based catchall validation + * (Zod v4 no longer provides ctx.path in preprocess). + * + * Reusable pattern for route validateSearch: filter first, then parse with a schema that has + * .catchall(z.union([z.string(), z.array(z.string()), z.undefined()])). + */ +export function filterSearchParamsByPrefix( + raw: Record, + knownKeys: string[], + allowedPrefixes: string[] +): Record { + const result: Record = {} + for (const key of knownKeys) { + if (Object.prototype.hasOwnProperty.call(raw, key)) { + result[key] = raw[key] + } + } + for (const key of Object.keys(raw)) { + if (allowedPrefixes.some((p) => key.startsWith(p))) { + result[key] = raw[key] + } + } + return result +} diff --git a/apps/supernova/src/routes/alerts.tsx b/apps/supernova/src/routes/alerts.tsx index 10a6f3c855..4bf24e57cc 100644 --- a/apps/supernova/src/routes/alerts.tsx +++ b/apps/supernova/src/routes/alerts.tsx @@ -14,6 +14,9 @@ import { ACTIVE_FILTERS_PREFIX, PAUSED_FILTERS_PREFIX } from "../constants" import { convertUrlStateToAppState, getFiltersForUrl } from "../lib/urlStateUtils" import { useGlobalsActions, useFilterActions, useGlobalsInitialFiltersApplied } from "../components/StoreProvider" import { isObjectWithKeys } from "../lib/utils" +import { filterSearchParamsByPrefix } from "../lib/utils" + +const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined()]) const searchSchema = z .object({ @@ -21,25 +24,18 @@ const searchSchema = z showDetailsFor: z.string().optional(), predefinedFilter: z.string().optional(), }) - .catchall( - z.preprocess( - (val, ctx) => { - if ( - ctx.path.length > 0 && - typeof ctx.path[0] === "string" && - !ctx.path[0].startsWith(ACTIVE_FILTERS_PREFIX) && - !ctx.path[0].startsWith(PAUSED_FILTERS_PREFIX) - ) { - return undefined - } - return val - }, - z.union([z.string(), z.array(z.string()), z.undefined()]) - ) - ) + .catchall(filterValueSchema) + +function validateAlertsSearch(search: Record): z.infer { + const filtered = filterSearchParamsByPrefix(search, Object.keys(searchSchema.shape), [ + ACTIVE_FILTERS_PREFIX, + PAUSED_FILTERS_PREFIX, + ]) + return searchSchema.parse(filtered) +} export const Route = createFileRoute("/alerts")({ - validateSearch: searchSchema, + validateSearch: validateAlertsSearch, beforeLoad: ({ search }) => { // extract alerts specific state from the URL search params const { activeFilters, pausedFilters, predefinedFilter, searchTerm, showDetailsFor } = diff --git a/package.json b/package.json index 65b6c23d69..118cb1cd31 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,6 @@ "packageManager": "pnpm@10.28.2", "dependencies": { "@tanstack/react-query": "5.89.0", - "zod": "3.25.76" + "zod": "4.3.6" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3e99b2828f..d7a32a13c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: 5.89.0 version: 5.89.0(react@19.2.3) zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.3.6 + version: 4.3.6 devDependencies: '@changesets/cli': specifier: 2.29.7 @@ -742,7 +742,7 @@ importers: version: 9.1.2(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-prettier: specifier: 5.5.4 - version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.6.2) + version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.8.0) eslint-plugin-react: specifier: 7.37.5 version: 7.37.5(eslint@9.39.2(jiti@2.6.1)) @@ -766,10 +766,10 @@ importers: version: 7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0) vite-plugin-dts: specifier: 4.5.4 - version: 4.5.4(@types/node@24.10.4)(rollup@4.50.1)(typescript@5.9.2)(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0)) + version: 4.5.4(@types/node@24.10.4)(rollup@4.50.1)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0)) vitest: specifier: 3.2.4 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.2))(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.3))(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0) packages/messages-provider: dependencies: @@ -915,7 +915,7 @@ importers: version: 4.7.0(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0)) eslint-plugin-storybook: specifier: 9.1.17 - version: 9.1.17(eslint@9.35.0(jiti@2.6.1))(storybook@9.1.17(@testing-library/dom@10.4.0)(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.3))(prettier@3.8.0)(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0)))(typescript@5.9.3) + version: 9.1.17(eslint@9.39.2(jiti@2.6.1))(storybook@9.1.17(@testing-library/dom@10.4.0)(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.3))(prettier@3.8.0)(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0)))(typescript@5.9.3) flatpickr: specifier: 4.6.13 version: 4.6.13 @@ -7503,6 +7503,9 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zustand@4.5.7: resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} engines: {node: '>=12.7.0'} @@ -10704,13 +10707,13 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@8.51.0(eslint@9.35.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.51.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) '@typescript-eslint/scope-manager': 8.51.0 '@typescript-eslint/types': 8.51.0 '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) - eslint: 9.35.0(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -10771,15 +10774,6 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.2))(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0))': - dependencies: - '@vitest/spy': 3.2.4 - estree-walker: 3.0.3 - magic-string: 0.30.19 - optionalDependencies: - msw: 2.12.7(@types/node@24.10.4)(typescript@5.9.2) - vite: 7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0) - '@vitest/mocker@3.2.4(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0))': dependencies: '@vitest/spy': 3.2.4 @@ -10856,19 +10850,6 @@ snapshots: de-indent: 1.0.2 he: 1.2.0 - '@vue/language-core@2.2.0(typescript@5.9.2)': - dependencies: - '@volar/language-core': 2.4.14 - '@vue/compiler-dom': 3.5.14 - '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.14 - alien-signals: 0.4.14 - minimatch: 9.0.5 - muggle-string: 0.4.1 - path-browserify: 1.0.1 - optionalDependencies: - typescript: 5.9.2 - '@vue/language-core@2.2.0(typescript@5.9.3)': dependencies: '@volar/language-core': 2.4.14 @@ -11944,16 +11925,6 @@ snapshots: dependencies: eslint: 9.39.2(jiti@2.6.1) - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.6.2): - dependencies: - eslint: 9.39.2(jiti@2.6.1) - prettier: 3.6.2 - prettier-linter-helpers: 1.0.0 - synckit: 0.11.8 - optionalDependencies: - '@types/eslint': 9.6.1 - eslint-config-prettier: 9.1.2(eslint@9.39.2(jiti@2.6.1)) - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.8.0): dependencies: eslint: 9.39.2(jiti@2.6.1) @@ -11990,10 +11961,10 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-storybook@9.1.17(eslint@9.35.0(jiti@2.6.1))(storybook@9.1.17(@testing-library/dom@10.4.0)(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.3))(prettier@3.8.0)(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0)))(typescript@5.9.3): + eslint-plugin-storybook@9.1.17(eslint@9.39.2(jiti@2.6.1))(storybook@9.1.17(@testing-library/dom@10.4.0)(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.3))(prettier@3.8.0)(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0)))(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 8.51.0(eslint@9.35.0(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.35.0(jiti@2.6.1) + '@typescript-eslint/utils': 8.51.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) storybook: 9.1.17(@testing-library/dom@10.4.0)(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.3))(prettier@3.8.0)(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0)) transitivePeerDependencies: - supports-color @@ -13519,32 +13490,6 @@ snapshots: ms@2.1.3: {} - msw@2.12.7(@types/node@24.10.4)(typescript@5.9.2): - dependencies: - '@inquirer/confirm': 5.1.10(@types/node@24.10.4) - '@mswjs/interceptors': 0.40.0 - '@open-draft/deferred-promise': 2.2.0 - '@types/statuses': 2.0.6 - cookie: 1.0.2 - graphql: 16.12.0 - headers-polyfill: 4.0.3 - is-node-process: 1.2.0 - outvariant: 1.4.3 - path-to-regexp: 6.3.0 - picocolors: 1.1.1 - rettime: 0.7.0 - statuses: 2.0.2 - strict-event-emitter: 0.5.1 - tough-cookie: 6.0.0 - type-fest: 5.3.0 - until-async: 3.0.2 - yargs: 17.7.2 - optionalDependencies: - typescript: 5.9.2 - transitivePeerDependencies: - - '@types/node' - optional: true - msw@2.12.7(@types/node@24.10.4)(typescript@5.9.3): dependencies: '@inquirer/confirm': 5.1.10(@types/node@24.10.4) @@ -14977,25 +14922,6 @@ snapshots: - tsx - yaml - vite-plugin-dts@4.5.4(@types/node@24.10.4)(rollup@4.50.1)(typescript@5.9.2)(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0)): - dependencies: - '@microsoft/api-extractor': 7.52.8(@types/node@24.10.4) - '@rollup/pluginutils': 5.3.0(rollup@4.50.1) - '@volar/typescript': 2.4.14 - '@vue/language-core': 2.2.0(typescript@5.9.2) - compare-versions: 6.1.1 - debug: 4.4.1 - kolorist: 1.8.0 - local-pkg: 1.1.1 - magic-string: 0.30.19 - typescript: 5.9.2 - optionalDependencies: - vite: 7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0) - transitivePeerDependencies: - - '@types/node' - - rollup - - supports-color - vite-plugin-dts@4.5.4(@types/node@24.10.4)(rollup@4.50.1)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0)): dependencies: '@microsoft/api-extractor': 7.52.8(@types/node@24.10.4) @@ -15063,50 +14989,6 @@ snapshots: tsx: 4.19.4 yaml: 2.8.0 - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.2))(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0): - dependencies: - '@types/chai': 5.2.2 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.2))(vite@7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.2.0 - debug: 4.4.1 - expect-type: 1.2.1 - magic-string: 0.30.19 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.9.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0) - vite-node: 3.2.4(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/debug': 4.1.12 - '@types/node': 24.10.4 - '@vitest/ui': 3.2.4(vitest@3.2.4) - jsdom: 26.1.0 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(msw@2.12.7(@types/node@24.10.4)(typescript@5.9.3))(sass@1.81.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.0): dependencies: '@types/chai': 5.2.2 @@ -15357,6 +15239,8 @@ snapshots: zod@3.25.76: {} + zod@4.3.6: {} + zustand@4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3): dependencies: use-sync-external-store: 1.5.0(react@19.2.3)