From 3f86b106188729dda18f02fa7d6236b8c5fbd20d Mon Sep 17 00:00:00 2001 From: joshunrau Date: Tue, 6 Oct 2026 14:51:38 -0400 Subject: [PATCH 01/14] build: upgrade to libnest 10, NestJS 12 and Vitest 5 libnest 10 requires NestJS 12, reflect-metadata ^0.2.2 and Vitest 5. apps/api moves @nestjs/common, core, platform-fastify and testing to 12.1 and reflect-metadata to 0.2.2; the root moves vitest and @vitest/coverage-v8 to 5.0. The lockfile holds one copy of each. @nestjs/platform-express is dropped: nothing in apps/api imports it, and libnest 10 no longer bundles it. The fastify@5 override goes as well. It only existed to lift the fastify 5.11.3 that platform-fastify 11 pinned, and platform-fastify 12.1 pins 5.12.5 itself. Vitest 5 reads a `projects` field inside a project config as nested projects. Every project config merged the root default export, so it inherited the root `projects` list and every run stopped at startup. The root config now exports a named `baseConfig` (include and watch) that the project configs merge, and its default export adds coverage and projects on top. A package's own `test` script now works too. Vitest 5 also defaults `clearMocks` to true, which erases calls made before the first test runs. The serve-instrument client test asserted on the hydrateRoot call made by the import in its beforeAll, and the gateway development server test on the vite createServer call made at import time. Both now copy those calls in beforeAll. V8 coverage now counts `??` as a branch, so series-overview gains a test ordering two series shared by every group, the only rows that reach its `?? ''` fallbacks. Vitest 5 reporters write to .vitest/, which is now ignored. The vitest project playbook and the testing strategy doc describe the new config pattern and the clearMocks default. Co-Authored-By: Claude Opus 5.5 --- .agents/docs/architecture/testing-strategy.md | 5 + .agents/docs/playbooks/add-vitest-project.md | 28 +- .gitignore | 1 + apps/api/package.json | 13 +- apps/api/vitest.config.ts | 2 +- .../__tests__/server.development.test.ts | 10 +- apps/gateway/vitest.config.ts | 2 +- apps/playground/vitest.config.ts | 2 +- .../utils/__tests__/series-overview.test.ts | 8 + apps/web/vitest.config.ts | 2 +- package.json | 5 +- packages/instrument-bundler/vitest.config.ts | 2 +- .../instrument-interpreter/vitest.config.ts | 2 +- packages/instrument-library/vitest.config.ts | 2 +- packages/instrument-stubs/vitest.config.ts | 2 +- packages/instrument-utils/vitest.config.ts | 2 +- packages/playground-url/vitest.config.ts | 2 +- packages/react-core/vitest.config.ts | 2 +- packages/release-info/vitest.config.ts | 2 +- packages/runtime-bundler/vitest.config.ts | 2 +- packages/runtime-core/vitest.config.ts | 2 +- packages/runtime-internal/vitest.config.ts | 2 +- packages/runtime-meta/vitest.config.ts | 2 +- packages/schemas/vitest.config.ts | 2 +- .../src/__tests__/client.test.tsx | 7 +- packages/serve-instrument/vitest.config.ts | 2 +- packages/subject-utils/vitest.config.ts | 2 +- packages/vite-plugin-runtime/vitest.config.ts | 2 +- pnpm-lock.yaml | 731 ++++++------------ runtime/v1/vitest.config.ts | 2 +- vitest.config.ts | 68 +- 31 files changed, 329 insertions(+), 589 deletions(-) diff --git a/.agents/docs/architecture/testing-strategy.md b/.agents/docs/architecture/testing-strategy.md index 8be9ddc7e..b3f5cfe35 100644 --- a/.agents/docs/architecture/testing-strategy.md +++ b/.agents/docs/architecture/testing-strategy.md @@ -63,6 +63,11 @@ project globs by design — `testing/` is Playwright, not vitest. - **There are no setup files anywhere in the repo.** No `setupFiles`, no `globalSetup`. Whatever a test needs, it arranges in its own body. +- **Every mock's recorded calls are cleared before each test** — Vitest's `clearMocks` defaults to + `true`, and no config here overrides it. A call made at import time or in `beforeAll` is gone by + the time an `it` runs, so copy what the assertion reads into a variable at the end of `beforeAll`, + as `packages/serve-instrument/src/__tests__/client.test.tsx` does. Implementations survive the + clear. - **`@testing-library/jest-dom` is not installed.** `toBeInTheDocument()` does not exist; assert `expect(screen.getByText(x)).toBeTruthy()`. - Workspace packages export TypeScript source from `exports` (`@opendatacapture/schemas` maps diff --git a/.agents/docs/playbooks/add-vitest-project.md b/.agents/docs/playbooks/add-vitest-project.md index da582ff6e..a2c7cb5fe 100644 --- a/.agents/docs/playbooks/add-vitest-project.md +++ b/.agents/docs/playbooks/add-vitest-project.md @@ -34,7 +34,7 @@ of your file means the config is absent. ```ts import { defineProject, mergeConfig } from 'vitest/config'; - import baseConfig from '../../vitest.config'; + import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, @@ -52,6 +52,14 @@ of your file means the config is absent. rather than the repo. Every project file uses `defineProject` — `defineConfig` belongs to the root config alone. + Merge the named `baseConfig`, never the root's default export. The default export adds `coverage` + and `projects`, and Vitest reads a `projects` field inside a project config as a nested set of + projects, re-resolved against the package directory, so every run stops before collecting a file: + + ``` + Error: No projects were found in "packages/schemas/vitest.config.ts". Make sure your configuration is correct. + ``` + 3. **Name the project after the directory.** The `name` field is the string `--project` takes, and it is the directory basename everywhere except `runtime/v1`, whose project is `runtime-v1` because `v1` names nothing on its own. Kebab-case, no `@opendatacapture/` scope. @@ -76,19 +84,11 @@ of your file means the config is absent. `packages/runtime-bundler` and `packages/runtime-meta` do — a `test/` directory outside the tsconfig `include` is type-checked by nothing. -6. **Add the `test` script, and do not rely on it.** `"test": "vitest"` under `packages/` and - `runtime/`, `"test": "env-cmd -f ../../.env vitest"` for an app, matching every project that - already exists. Running it does not work today: `mergeConfig` inherits the root `test.projects` - globs along with everything else, and started from inside the package they re-resolve against that - directory and match nothing — - - ``` - Error: No projects were found. Make sure your configuration is correct. The projects definition: […] - ``` - - Scope a run from the repo root instead: `pnpm exec vitest --project `. Root `pnpm test` runs - the projects directly and never reads this script; `vitest` is a root devDependency, so the package - declares nothing. +6. **Add the `test` script.** `"test": "vitest"` under `packages/` and `runtime/`, + `"test": "env-cmd -f ../../.env vitest"` for an app, matching every project that already exists. + `pnpm --filter @opendatacapture/ test` runs this one project from the package directory. Root + `pnpm test` runs the projects directly and never reads this script; `vitest` is a root + devDependency, so the package declares nothing. 7. **Place the test file inside the inherited glob.** That is `**/*.{test,spec}.?(c|m)[jt]s?(x)`, relative to the package `root`; the house convention is a `__tests__/` folder beside the subject, diff --git a/.gitignore b/.gitignore index 9fda10d4a..eb6b72b8d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ node_modules # testing coverage +.vitest # build build diff --git a/apps/api/package.json b/apps/api/package.json index 16aeb95ca..83e29425f 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -21,17 +21,16 @@ "@casl/prisma": "^1.5.1", "@douglasneuroinformatics/libcrypto": "catalog:", "@douglasneuroinformatics/libjs": "catalog:", - "@douglasneuroinformatics/libnest": "^9.0.0", + "@douglasneuroinformatics/libnest": "^10.0.0", "@douglasneuroinformatics/libpasswd": "catalog:", "@douglasneuroinformatics/libstats": "catalog:", "@faker-js/faker": "~10.2.0", "@nestjs/axios": "^12.0.1", - "@nestjs/common": "^11.2.6", - "@nestjs/core": "^11.2.6", + "@nestjs/common": "^12.1.1", + "@nestjs/core": "^12.1.1", "@nestjs/jwt": "^12.0.2", "@nestjs/passport": "^12.0.0", - "@nestjs/platform-express": "^11.2.6", - "@nestjs/platform-fastify": "^11.2.6", + "@nestjs/platform-fastify": "^12.1.1", "@nestjs/throttler": "^6.7.1", "@opendatacapture/demo": "workspace:*", "@opendatacapture/instrument-bundler": "workspace:*", @@ -54,13 +53,13 @@ "nodemailer": "catalog:", "passport": "^0.7.0", "passport-jwt": "^4.0.1", - "reflect-metadata": "^0.1.14", + "reflect-metadata": "^0.2.2", "rxjs": "^7.8.2", "ts-pattern": "workspace:ts-pattern__5.x@*", "zod": "workspace:zod__3.x@*" }, "devDependencies": { - "@nestjs/testing": "^11.2.6", + "@nestjs/testing": "^12.1.1", "@types/express": "^5.0.0", "@types/passport": "^1.0.17", "@types/passport-jwt": "^4.0.1", diff --git a/apps/api/vitest.config.ts b/apps/api/vitest.config.ts index 8c09a176c..b0c6b27d3 100644 --- a/apps/api/vitest.config.ts +++ b/apps/api/vitest.config.ts @@ -4,7 +4,7 @@ import libnest from '@douglasneuroinformatics/libnest/testing/plugin'; import { defineProject, mergeConfig } from 'vitest/config'; import type { Plugin } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; const [swcPlugin, libnestPlugin] = libnest({ baseUrl: path.resolve(import.meta.dirname, 'src'), diff --git a/apps/gateway/src/server/__tests__/server.development.test.ts b/apps/gateway/src/server/__tests__/server.development.test.ts index 43cd79c85..9360812ce 100644 --- a/apps/gateway/src/server/__tests__/server.development.test.ts +++ b/apps/gateway/src/server/__tests__/server.development.test.ts @@ -2,7 +2,7 @@ import { once } from 'events'; import fs from 'fs'; import type { Server } from 'http'; -import { afterAll, afterEach, describe, expect, it, vi } from 'vitest'; +import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest'; import { config } from '@/config'; import type { RenderFunction } from '@/entry-server'; @@ -60,6 +60,12 @@ class TestDevelopmentServer extends DevelopmentServer { const openServers: Server[] = []; +let createServerCalls: unknown[][]; + +beforeAll(() => { + createServerCalls = [...createServer.mock.calls]; +}); + afterEach(async () => { await Promise.all( openServers.splice(0).map((httpServer) => { @@ -78,7 +84,7 @@ afterAll(() => { describe('DevelopmentServer', () => { it('should create vite in middleware mode, so express stays in charge of the http server', () => { - expect(createServer).toHaveBeenCalledWith({ appType: 'custom', server: { middlewareMode: true } }); + expect(createServerCalls).toStrictEqual([[{ appType: 'custom', server: { middlewareMode: true } }]]); }); it('should mount the vite middlewares, so client assets are served with hot reload', async () => { diff --git a/apps/gateway/vitest.config.ts b/apps/gateway/vitest.config.ts index 3eb07a276..ef65e1631 100644 --- a/apps/gateway/vitest.config.ts +++ b/apps/gateway/vitest.config.ts @@ -2,7 +2,7 @@ import path from 'path'; import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/apps/playground/vitest.config.ts b/apps/playground/vitest.config.ts index 9ae298ccb..451529353 100644 --- a/apps/playground/vitest.config.ts +++ b/apps/playground/vitest.config.ts @@ -2,7 +2,7 @@ import * as path from 'node:path'; import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/apps/web/src/utils/__tests__/series-overview.test.ts b/apps/web/src/utils/__tests__/series-overview.test.ts index 099324dbc..b977341a2 100644 --- a/apps/web/src/utils/__tests__/series-overview.test.ts +++ b/apps/web/src/utils/__tests__/series-overview.test.ts @@ -29,4 +29,12 @@ describe('sortSeriesOverviewRows', () => { ]; expect(titles(sortSeriesOverviewRows(rows))).toEqual(['Follow-up', 'Intake']); }); + + it('should order the series every group shares by title', () => { + const rows = [ + { groupName: null, title: 'Intake' }, + { groupName: null, title: 'Follow-up' } + ]; + expect(titles(sortSeriesOverviewRows(rows))).toEqual(['Follow-up', 'Intake']); + }); }); diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts index f6a0fe91b..e18c732e6 100644 --- a/apps/web/vitest.config.ts +++ b/apps/web/vitest.config.ts @@ -2,7 +2,7 @@ import * as path from 'node:path'; import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/package.json b/package.json index c3f26fe1b..effc67bc0 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@douglasneuroinformatics/tsconfig": "^2.0.0", "@types/js-yaml": "^4.0.9", "@types/node": "24.x", - "@vitest/coverage-v8": "^4.1.11", + "@vitest/coverage-v8": "^5.0.2", "conventional-commits-parser": "catalog:", "env-cmd": "^11.0.0", "eslint": "9.39.x", @@ -55,7 +55,7 @@ "tsx": "catalog:", "turbo": "^2.11.5", "typescript": "6.0.x", - "vitest": "^4.1.11" + "vitest": "^5.0.2" }, "os": [ "darwin", @@ -70,7 +70,6 @@ "@types/react": "-", "@types/react-dom": "-", "find-my-way@9": "^9.7.0", - "fastify@5": "^5.12.5", "typescript": "6.0.x", "yaml@2": "^2.9.0" }, diff --git a/packages/instrument-bundler/vitest.config.ts b/packages/instrument-bundler/vitest.config.ts index 841b15c56..a1d88f895 100644 --- a/packages/instrument-bundler/vitest.config.ts +++ b/packages/instrument-bundler/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/instrument-interpreter/vitest.config.ts b/packages/instrument-interpreter/vitest.config.ts index a9b387b5b..c0f8611dc 100644 --- a/packages/instrument-interpreter/vitest.config.ts +++ b/packages/instrument-interpreter/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/instrument-library/vitest.config.ts b/packages/instrument-library/vitest.config.ts index 9ef25a994..6ce5c8e4b 100644 --- a/packages/instrument-library/vitest.config.ts +++ b/packages/instrument-library/vitest.config.ts @@ -2,7 +2,7 @@ import path from 'node:path'; import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; const repositoryRoot = path.resolve(import.meta.dirname, '../..'); diff --git a/packages/instrument-stubs/vitest.config.ts b/packages/instrument-stubs/vitest.config.ts index a23e1ed06..25e8e19a3 100644 --- a/packages/instrument-stubs/vitest.config.ts +++ b/packages/instrument-stubs/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/instrument-utils/vitest.config.ts b/packages/instrument-utils/vitest.config.ts index 9f8df1976..968453077 100644 --- a/packages/instrument-utils/vitest.config.ts +++ b/packages/instrument-utils/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/playground-url/vitest.config.ts b/packages/playground-url/vitest.config.ts index 3e8757e15..7af8a0bbc 100644 --- a/packages/playground-url/vitest.config.ts +++ b/packages/playground-url/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/react-core/vitest.config.ts b/packages/react-core/vitest.config.ts index 61d46619c..1b74e5816 100644 --- a/packages/react-core/vitest.config.ts +++ b/packages/react-core/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/release-info/vitest.config.ts b/packages/release-info/vitest.config.ts index 7791d9975..c074e4c0c 100644 --- a/packages/release-info/vitest.config.ts +++ b/packages/release-info/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/runtime-bundler/vitest.config.ts b/packages/runtime-bundler/vitest.config.ts index 7b4803403..dd0dd516c 100644 --- a/packages/runtime-bundler/vitest.config.ts +++ b/packages/runtime-bundler/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/runtime-core/vitest.config.ts b/packages/runtime-core/vitest.config.ts index 2d4e245f8..ad645b017 100644 --- a/packages/runtime-core/vitest.config.ts +++ b/packages/runtime-core/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config.js'; +import { baseConfig } from '../../vitest.config.js'; export default mergeConfig( baseConfig, diff --git a/packages/runtime-internal/vitest.config.ts b/packages/runtime-internal/vitest.config.ts index fe1a086b5..a88a8326e 100644 --- a/packages/runtime-internal/vitest.config.ts +++ b/packages/runtime-internal/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/runtime-meta/vitest.config.ts b/packages/runtime-meta/vitest.config.ts index b3f5ca561..8e5ea9019 100644 --- a/packages/runtime-meta/vitest.config.ts +++ b/packages/runtime-meta/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/schemas/vitest.config.ts b/packages/schemas/vitest.config.ts index 609fada7d..c468480e8 100644 --- a/packages/schemas/vitest.config.ts +++ b/packages/schemas/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/serve-instrument/src/__tests__/client.test.tsx b/packages/serve-instrument/src/__tests__/client.test.tsx index c6075228c..4485c838d 100644 --- a/packages/serve-instrument/src/__tests__/client.test.tsx +++ b/packages/serve-instrument/src/__tests__/client.test.tsx @@ -17,19 +17,22 @@ vi.mock('react-dom/client', () => ({ hydrateRoot })); const ROOT_PROPS: RootProps = { instruments: [{ name: 'happiness', type: 'forms' }], page: 'index' }; describe('client', () => { + let hydrateRootArgs: Parameters | undefined; + beforeAll(async () => { document.body.innerHTML = '
'; vi.stubGlobal('__ROOT_PROPS__', ROOT_PROPS); await import('../client'); vi.unstubAllGlobals(); + hydrateRootArgs = hydrateRoot.mock.lastCall; }); it('should hydrate the server-rendered #root element, so the SSR markup is reused rather than replaced', () => { - expect(hydrateRoot.mock.lastCall?.[0]).toBe(document.getElementById('root')); + expect(hydrateRootArgs?.[0]).toBe(document.getElementById('root')); }); it('should hydrate Root in strict mode with the server-injected props, so the client tree matches the SSR markup', () => { - expect(hydrateRoot.mock.lastCall?.[1]).toMatchObject({ + expect(hydrateRootArgs?.[1]).toMatchObject({ props: { children: { props: ROOT_PROPS, type: Root } }, type: StrictMode }); diff --git a/packages/serve-instrument/vitest.config.ts b/packages/serve-instrument/vitest.config.ts index 5384005df..090f93b81 100644 --- a/packages/serve-instrument/vitest.config.ts +++ b/packages/serve-instrument/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/subject-utils/vitest.config.ts b/packages/subject-utils/vitest.config.ts index 1ae50285f..9a5dcb079 100644 --- a/packages/subject-utils/vitest.config.ts +++ b/packages/subject-utils/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/packages/vite-plugin-runtime/vitest.config.ts b/packages/vite-plugin-runtime/vitest.config.ts index dde258cb1..1324f13fd 100644 --- a/packages/vite-plugin-runtime/vitest.config.ts +++ b/packages/vite-plugin-runtime/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 426e3addc..d2fe8f39e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -129,7 +129,6 @@ overrides: '@types/react': '-' '@types/react-dom': '-' find-my-way@9: ^9.7.0 - fastify@5: ^5.12.5 typescript: 6.0.x yaml@2: ^2.9.0 @@ -159,8 +158,8 @@ importers: specifier: 24.x version: 24.19.0 '@vitest/coverage-v8': - specifier: ^4.1.11 - version: 4.1.11(@vitest/browser@4.1.11)(vitest@4.1.11) + specifier: ^5.0.2 + version: 5.0.2(vitest@5.0.2) conventional-commits-parser: specifier: 'catalog:' version: 7.1.2 @@ -204,8 +203,8 @@ importers: specifier: 6.0.x version: 6.0.3 vitest: - specifier: ^4.1.11 - version: 4.1.11(@types/node@24.19.0)(@vitest/coverage-v8@4.1.11)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + specifier: ^5.0.2 + version: 5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) apps/api: dependencies: @@ -228,8 +227,8 @@ importers: specifier: 'catalog:' version: 3.2.1(neverthrow@8.2.0)(zod@vendor+zod@3.x) '@douglasneuroinformatics/libnest': - specifier: ^9.0.0 - version: 9.0.0(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(@nestjs/platform-fastify@11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6))(@nestjs/testing@11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(@nestjs/platform-express@11.2.6))(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3))(@swc/types@0.1.28)(fastify@5.12.5)(neverthrow@8.2.0)(reflect-metadata@0.1.14)(rollup@4.61.1)(rxjs@7.8.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))(vitest@4.1.11)(zod@vendor+zod@3.x) + specifier: ^10.0.0 + version: 10.0.0(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-fastify@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@nestjs/testing@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3))(@swc/types@0.1.28)(fastify@5.12.5)(neverthrow@8.2.0)(reflect-metadata@0.2.2)(rollup@4.61.1)(rxjs@7.8.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))(vitest@5.0.2)(zod@vendor+zod@3.x) '@douglasneuroinformatics/libpasswd': specifier: 'catalog:' version: 0.0.3(typescript@6.0.3) @@ -241,28 +240,25 @@ importers: version: 10.2.0 '@nestjs/axios': specifier: ^12.0.1 - version: 12.0.1(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(axios@1.20.0)(rxjs@7.8.2) + version: 12.0.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.20.0)(rxjs@7.8.2) '@nestjs/common': - specifier: ^11.2.6 - version: 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) + specifier: ^12.1.1 + version: 12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': - specifier: ^11.2.6 - version: 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@11.2.6)(reflect-metadata@0.1.14)(rxjs@7.8.2) + specifier: ^12.1.1 + version: 12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/jwt': specifier: ^12.0.2 - version: 12.0.2(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2)) + version: 12.0.2(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2)) '@nestjs/passport': specifier: ^12.0.0 - version: 12.0.0(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(passport@0.7.0) - '@nestjs/platform-express': - specifier: ^11.2.6 - version: 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6) + version: 12.0.0(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) '@nestjs/platform-fastify': - specifier: ^11.2.6 - version: 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6) + specifier: ^12.1.1 + version: 12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)) '@nestjs/throttler': specifier: ^6.7.1 - version: 6.7.1(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(reflect-metadata@0.1.14) + version: 6.7.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2) '@opendatacapture/demo': specifier: workspace:* version: link:../../packages/demo @@ -327,8 +323,8 @@ importers: specifier: ^4.0.1 version: 4.0.1 reflect-metadata: - specifier: ^0.1.14 - version: 0.1.14 + specifier: ^0.2.2 + version: 0.2.2 rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -340,8 +336,8 @@ importers: version: link:../../vendor/zod@3.x devDependencies: '@nestjs/testing': - specifier: ^11.2.6 - version: 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(@nestjs/platform-express@11.2.6) + specifier: ^12.1.1 + version: 12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)) '@types/express': specifier: ^5.0.0 version: 5.0.6 @@ -1955,11 +1951,6 @@ packages: resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.7': - resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} - engines: {node: '>=6.0.0'} - hasBin: true - '@babel/parser@7.29.9': resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} engines: {node: '>=6.0.0'} @@ -1977,10 +1968,6 @@ packages: resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} - '@babel/types@7.29.7': - resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} - engines: {node: '>=6.9.0'} - '@babel/types@7.29.8': resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} @@ -2016,9 +2003,6 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@blazediff/core@1.9.1': - resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} - '@borewit/text-codec@0.2.2': resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} @@ -2166,22 +2150,22 @@ packages: neverthrow: ^8.2.0 zod: ^3.25.67 || 4.x - '@douglasneuroinformatics/libnest@9.0.0': - resolution: {integrity: sha512-uVFJEin/mkhW+FvvGM0032qXpnv5T2astzAGCziesGvBGWAf6PG0TQptl5WQcNUIptdN+GR/O7co813jy8I5yQ==} - engines: {node: 22.x || 24.x} + '@douglasneuroinformatics/libnest@10.0.0': + resolution: {integrity: sha512-q3ZyuH9zVagdeMj7nthIA8/7p5pbci0c6tB+cWN//X3O1HwrHEt+TkXwsg/fRaBIglok9RB3qN4X6lBF3LKb8A==} + engines: {node: ^22.12.0 || 24.x} hasBin: true peerDependencies: - '@nestjs/common': ^11.0.11 - '@nestjs/core': ^11.0.11 - '@nestjs/platform-fastify': ^11.1.6 - '@nestjs/testing': ^11.0.11 + '@nestjs/common': ^12.0.0 + '@nestjs/core': ^12.0.0 + '@nestjs/platform-fastify': ^12.0.0 + '@nestjs/testing': ^12.0.0 '@prisma/client': ^6.9.0 - fastify: ^5.12.5 + fastify: ^5.12.1 neverthrow: ^8.2.0 - reflect-metadata: ~0.1.13 + reflect-metadata: ^0.2.2 rxjs: ^7.8.2 vite: '*' - vitest: 4.x + vitest: 5.x zod: ^3.25.x peerDependenciesMeta: vite: @@ -2876,8 +2860,8 @@ packages: '@fastify/fast-json-stringify-compiler@5.0.3': resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==} - '@fastify/formbody@8.0.2': - resolution: {integrity: sha512-84v5J2KrkXzjgBpYnaNRPqwgMsmY7ZDjuj0YVuMR3NXCJRCgKEZy/taSP1wUYGn0onfxJpLyRGDLa+NMaDJtnA==} + '@fastify/formbody@9.0.0': + resolution: {integrity: sha512-T/af26CSrUARBCvsEmv+DJLPfZlrRKESzqironxP1j7qzuLyKcoZtj6MuTGShuKx1THXugoie2oFbUJxXfGFzA==} '@fastify/forwarded@3.0.1': resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==} @@ -3142,6 +3126,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -3254,9 +3241,6 @@ packages: '@microsoft/tsdoc-config@0.18.2': resolution: {integrity: sha512-4ymuLTQ9lFwJXK15f41iPcNHflymjk02Ltq5f5wo3fA02O1nHIGSc1TIZwzTSQZep+jXHM644jobt4Q8O3vtqg==} - '@microsoft/tsdoc@0.16.0': - resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} - '@microsoft/tsdoc@0.17.0': resolution: {integrity: sha512-p68VexhnH7ojf3U27RdryUDqnKJgKaWUbCL96vJR7N0mqDuhKG5OIECk3sCPPdynxjk+yoVhRkScqtv5KQjzsw==} @@ -3324,8 +3308,8 @@ packages: axios: ^1.3.1 rxjs: ^7.0.0 - '@nestjs/common@11.2.6': - resolution: {integrity: sha512-uXs98qEOesTf6rGs0OcvFpRdF4M6JvzyBmoIXcTYvojdte4HiwDYv+whaALoboyfu/fuq2umF74FLQu3V7ZPHw==} + '@nestjs/common@12.1.1': + resolution: {integrity: sha512-foR+9SDz53vd4WXvN5UEXbcU0i/i9mf/zXqbAWlEDQlxAooS4kZoY5X/Zr2DsUJZaLPOk3kFQdu6EhbWrhAoDQ==} peerDependencies: class-transformer: '>=0.4.1' class-validator: '>=0.13.2' @@ -3337,14 +3321,14 @@ packages: class-validator: optional: true - '@nestjs/core@11.2.6': - resolution: {integrity: sha512-KZkwy6KBePPeT3EUj+NACVq6VLT5S+Msbc761NAVVyQ0FyOj7o4I85GEg/cdEPu6KdLqhJFfuqRW2SN+J2ZubQ==} + '@nestjs/core@12.1.1': + resolution: {integrity: sha512-wI6zlO7sfoL4532/XXVkdPSiiXPAUsFdUkK42bTxVKoaEj60x8y8k95HtxOy5q/tUcXcB2Q4HIFKwVjgMamHeg==} engines: {node: '>= 20'} peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/microservices': ^11.0.0 - '@nestjs/platform-express': ^11.0.0 - '@nestjs/websockets': ^11.0.0 + '@nestjs/common': ^12.0.0 + '@nestjs/microservices': ^12.0.0 + '@nestjs/platform-express': ^12.0.0 + '@nestjs/websockets': ^12.0.0 reflect-metadata: ^0.1.12 || ^0.2.0 rxjs: ^7.1.0 peerDependenciesMeta: @@ -3360,68 +3344,35 @@ packages: peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 - '@nestjs/mapped-types@2.1.1': - resolution: {integrity: sha512-SCCoMEJ6jdeI5h/N+KCVF1+pmg/hmEkNA5nHTS8Gvww7T/LCl4o1gFLinw2iQ60w7slFkszHcGLKGdazVI4F8A==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - class-transformer: ^0.4.0 || ^0.5.0 - class-validator: ^0.13.0 || ^0.14.0 || ^0.15.0 - reflect-metadata: ^0.1.12 || ^0.2.0 - peerDependenciesMeta: - class-transformer: - optional: true - class-validator: - optional: true - '@nestjs/passport@12.0.0': resolution: {integrity: sha512-FM9ioo2Dyj+cC7aeO4g19ijAXoRaHlW/GQZFbgXsM4Z9pyQedA8yOseb7ivfqlPwUKJlH9PFgeWniu6c0vb6dw==} peerDependencies: '@nestjs/common': ^11.0.0 || ^12.0.0 passport: ^0.5.0 || ^0.6.0 || ^0.7.0 - '@nestjs/platform-express@11.2.6': - resolution: {integrity: sha512-jPypI/+aueizkYG2Bw2kg8GaNsWE7ekFBSQ/Rwet7wEdBA/+q2FA364UbDabqjlR7C3VSNHmP/JXh+8+jXyDOw==} - peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/core': ^11.0.0 - - '@nestjs/platform-fastify@11.2.6': - resolution: {integrity: sha512-Ynx21MFs2EHjebQkVG4fHWLJr3pcjA+45qv4+YbwPtETP7gLvsk955UPviKgcq4zil9tvE799qilz9BUxTISFQ==} + '@nestjs/platform-fastify@12.1.1': + resolution: {integrity: sha512-7w5Cm/bI2f4fly5VmOhtKv25bwM8LoLpWGCB0fzPCTDRnEosIbLO3mWhBRHGAdPWE/N52LS14L0w+1/ZzsdFqw==} peerDependencies: + '@fastify/multipart': ^10.1.0 '@fastify/static': ^10.1.2 '@fastify/view': ^10.0.0 || ^11.0.0 || ^12.0.0 - '@nestjs/common': ^11.0.0 - '@nestjs/core': ^11.0.0 + '@nestjs/common': ^12.0.0 + '@nestjs/core': ^12.0.0 peerDependenciesMeta: - '@fastify/static': - optional: true - '@fastify/view': + '@fastify/multipart': optional: true - - '@nestjs/swagger@11.4.4': - resolution: {integrity: sha512-VaIo1ruV2G7b+f2zPzkBSUNy9a/WQ9sg8TLKhWlrTfg4O6U10M/PA7Xi6XMXadOVhwOqoesijba8jH3i/3adrA==} - peerDependencies: - '@fastify/static': ^8.0.0 || ^9.0.0 - '@nestjs/common': ^11.0.1 - '@nestjs/core': ^11.0.1 - class-transformer: '*' - class-validator: '*' - reflect-metadata: ^0.1.12 || ^0.2.0 - peerDependenciesMeta: '@fastify/static': optional: true - class-transformer: - optional: true - class-validator: + '@fastify/view': optional: true - '@nestjs/testing@11.2.6': - resolution: {integrity: sha512-Oua8EUw3WsnSIwAMIpvyuJxOdUxHeX04fX5MsSIpjaft5CcNnbWhSmM/u/GDDOEcAOrxOfG/KHa7LzzWdS/bxw==} + '@nestjs/testing@12.1.1': + resolution: {integrity: sha512-kMCTifsIlvfdCs/4F495HY0tjmwHu0d5JoXpr9BsT8CJ1NScfbrx24mlkevFNpq26jTQKR5e82hOfqikad1gSg==} peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/core': ^11.0.0 - '@nestjs/microservices': ^11.0.0 - '@nestjs/platform-express': ^11.0.0 + '@nestjs/common': ^12.0.0 + '@nestjs/core': ^12.0.0 + '@nestjs/microservices': ^12.0.0 + '@nestjs/platform-express': ^12.0.0 peerDependenciesMeta: '@nestjs/microservices': optional: true @@ -5146,9 +5097,6 @@ packages: resolution: {integrity: sha512-9oUS23ebDfbl8ED6CANdQ3qqBc/xhrQvzhQAv61oN391FvNRww6RzdUbo97NEXaMm7lNEQsTjdE3zqtgiMv/eQ==} engines: {node: '>=20.9.0'} - '@scarf/scarf@1.4.0': - resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} - '@shikijs/core@3.23.0': resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} @@ -6026,16 +5974,11 @@ packages: peerDependencies: vite: ^4 || ^5 || ^6 || ^7 || ^8 - '@vitest/browser@4.1.11': - resolution: {integrity: sha512-bwMovvAeuTFOK5kIFevw4VEf+1gVEICv4SYK4k3knJOxl6b1zEWud8mYKD73e1B0odAn174h1MofURy2TPWf3w==} + '@vitest/coverage-v8@5.0.2': + resolution: {integrity: sha512-3ffHBEi8DOOBLwIGBhOBZbRfYFYWjMUuxZicONdhuFEFEG5AVsMOSrVeuROskqnqpbOmEJwxM2eTVZ9N3a1t+A==} peerDependencies: - vitest: 4.1.11 - - '@vitest/coverage-v8@4.1.11': - resolution: {integrity: sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==} - peerDependencies: - '@vitest/browser': 4.1.11 - vitest: 4.1.11 + '@vitest/browser': 5.0.2 + vitest: 5.0.2 peerDependenciesMeta: '@vitest/browser': optional: true @@ -6043,11 +5986,16 @@ packages: '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@vitest/expect@4.1.11': - resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} + '@vitest/istanbul-lib-coverage@1.0.2': + resolution: {integrity: sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==} + engines: {node: '>=22'} + + '@vitest/istanbul-lib-report@1.0.2': + resolution: {integrity: sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==} + engines: {node: '>=22'} - '@vitest/mocker@4.1.11': - resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} + '@vitest/mocker@5.0.2': + resolution: {integrity: sha512-Z5FS00Q1SJHkB35xATsmWGdQ5WA1/0MV3CDjqyv7GavHv1OfOj145MNfHOlHk7QLes21dKFDHr8EO2zvL+9WGA==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -6060,27 +6008,15 @@ packages: '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/pretty-format@4.1.11': - resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} - - '@vitest/runner@4.1.11': - resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} - - '@vitest/snapshot@4.1.11': - resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} - '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/spy@4.1.11': - resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} + '@vitest/spy@5.0.2': + resolution: {integrity: sha512-Ijc7T1nT9efNb5LxvjaBrEqw3f/QwUv5EE0nKqZxgqsaV/FxAAZ8baGylA8X/Z2oS4Lp+K74Jr6dTJsDKxJDeg==} '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - '@vitest/utils@4.1.11': - resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} - '@volar/kit@2.4.28': resolution: {integrity: sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==} peerDependencies: @@ -6207,9 +6143,6 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - append-field@1.0.0: - resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} - are-docs-informative@0.0.2: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} @@ -6287,8 +6220,8 @@ packages: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} - ast-v8-to-istanbul@1.0.3: - resolution: {integrity: sha512-jCMQ6ZylLPudp0CDfBmQBZUsrh1/8psbmu9ibeVWKuHWD0YrH9YABwlKu5kVEFoT0GCQQW9Z/SxfuEbbkGQCRg==} + ast-v8-to-istanbul@1.0.7: + resolution: {integrity: sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==} astring@1.9.0: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} @@ -6489,10 +6422,6 @@ packages: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} - busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} - bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -6755,10 +6684,6 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cors@2.8.6: - resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} - engines: {node: '>= 0.10'} - cosmiconfig-typescript-loader@6.3.0: resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==} engines: {node: '>=v18'} @@ -7152,9 +7077,6 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - es-module-lexer@2.1.0: - resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} - es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} @@ -7488,9 +7410,6 @@ packages: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} - fastify-plugin@5.1.0: - resolution: {integrity: sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==} - fastify-plugin@6.0.0: resolution: {integrity: sha512-fZOty7z3O7vOliF6d8bHE3wiEh1KcNnKEQensSgTk9C1DvN6nRLS++XVd86v33Hw/8u9Un8A1zDrQ8ujcQDHEg==} @@ -7524,9 +7443,9 @@ packages: resolution: {integrity: sha512-v0g/PTeuQgvKCBrVRsfVudvwXlRHSWHEQkVgKawgCGHkEpKA1clp3Om5jvEVhz8G9W/mOYjJH9FhkH4C888PgQ==} engines: {node: '>= 22'} - file-type@21.3.4: - resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==} - engines: {node: '>=20'} + file-type@22.1.1: + resolution: {integrity: sha512-sEB0oStmeLGtpmSAD88RDNuLsvhv4iC3u0lE25CUvfCULvj49xoQ3l3K525Vn+ZzsZ6DOLajQCV6RmhwwhqiJg==} + engines: {node: '>=22'} fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} @@ -7587,10 +7506,6 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} - form-data@4.0.6: resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} engines: {node: '>= 6'} @@ -7861,9 +7776,6 @@ packages: resolution: {integrity: sha512-f7FxrlBz5FGd/J7tx9DR1cCQNIzKTl4BLZzgRXeQblzcO/xiWGrvXBMwztRzjJVeFACpED6P+iVmU7of9eFqyA==} engines: {node: '>= 20.19.0'} - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - html-escaper@3.0.3: resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} @@ -8140,18 +8052,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-reports@3.2.0: - resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} - engines: {node: '>=8'} - iterare@1.2.1: resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} engines: {node: '>=6'} @@ -8191,10 +8091,6 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - js-yaml@4.3.2: resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} hasBin: true @@ -8485,17 +8381,19 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.4.2: + resolution: {integrity: sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==} + magicast@0.5.3: resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + magicast@0.5.5: + resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==} + make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - markdown-extensions@2.0.0: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} @@ -8579,10 +8477,6 @@ packages: mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - media-typer@1.1.0: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} @@ -8887,10 +8781,6 @@ packages: muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} - multer@2.4.0: - resolution: {integrity: sha512-7dqa0ZcFfzbefdTuIkzOSMvZWC0J7FLqBOjJUZvDCXShIURWKxAyTT1wHhnE5q19c7jOJf43IYYKjBmZVZmvhg==} - engines: {node: '>= 10.16.0'} - nanoid@3.3.19: resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -9007,8 +8897,8 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} - obug@2.1.2: - resolution: {integrity: sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==} + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} engines: {node: '>=12.20.0'} ofetch@1.5.1: @@ -9246,10 +9136,6 @@ packages: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} - pngjs@7.0.0: - resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} - engines: {node: '>=14.19.0'} - possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -9645,8 +9531,8 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} - reflect-metadata@0.1.14: - resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} @@ -9958,9 +9844,6 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -10066,9 +9949,6 @@ packages: resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} engines: {node: '>=0.8'} - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - stacktrace-parser@0.1.11: resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} engines: {node: '>=6'} @@ -10080,8 +9960,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@4.1.0: - resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + std-env@4.3.0: + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} @@ -10102,10 +9982,6 @@ packages: vite-plus: optional: true - streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - streamx@2.26.0: resolution: {integrity: sha512-VvNG1K72Po/xwJzxZFnZ++Tbrv4lwSptsbkFuzXCJAYZvCK5nnxsvXU6ajqkv7chyiI1Y0YXq2Jh8Iy8Y7NF/A==} @@ -10237,9 +10113,6 @@ packages: engines: {node: '>=16'} hasBin: true - swagger-ui-dist@5.32.6: - resolution: {integrity: sha512-75ttZNaYCLoFPnozPZcTUU6mS3wKT8l7WLjU5zJSHFeJa23i5vtnze6IiCl4jDMPeQTXVXIgovq4M11NNfQvSA==} - synckit@0.11.13: resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -10283,13 +10156,18 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinybench@6.2.0: + resolution: {integrity: sha512-78U2TlB2CnVenajOFzf3BKSm0J6oz5L0NV7g32LCPccvYc0lbWvys4d3uUUCS2B1N8PAf2+aekR8i1KbC3HO7Q==} + engines: {node: '>=20.0.0'} tinyexec@1.2.4: resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} + tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} @@ -10298,8 +10176,8 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} engines: {node: '>=14.0.0'} tinyspy@4.0.4: @@ -10412,10 +10290,6 @@ packages: resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} engines: {node: '>=20'} - type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - type-is@2.1.0: resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} engines: {node: '>= 18'} @@ -10815,23 +10689,23 @@ packages: vite: optional: true - vitest@4.1.11: - resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + vitest@5.0.2: + resolution: {integrity: sha512-7MQrx9pDv5aHiUcovIb/70Ys3tgtkUVgCtledvKdCmEO+/1Dicq5ZqoSxOW034m03oqC+oHOKui2dM6qtMLoJg==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.11 - '@vitest/browser-preview': 4.1.11 - '@vitest/browser-webdriverio': 4.1.11 - '@vitest/coverage-istanbul': 4.1.11 - '@vitest/coverage-v8': 4.1.11 - '@vitest/ui': 4.1.11 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.2 + '@vitest/browser-preview': 5.0.2 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.2 + '@vitest/coverage-v8': 5.0.2 + '@vitest/ui': 5.0.2 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -11008,9 +10882,9 @@ packages: engines: {node: '>= 8'} hasBin: true - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} + why-is-node-running@3.2.2: + resolution: {integrity: sha512-NKUzAelcoCXhXL4dJzKIwXeR8iEVqsA0Lq6Vnd0UXvgaKbzVo4ZTHROF2Jidrv+SgxOQ03fMinnNhzZATxOD3A==} + engines: {node: '>=20.11'} hasBin: true widest-line@5.0.0: @@ -11532,7 +11406,7 @@ snapshots: '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.9 '@babel/template': 7.29.7 '@babel/traverse': 7.29.7 '@babel/types': 7.29.8 @@ -11547,7 +11421,7 @@ snapshots: '@babel/generator@7.29.7': dependencies: - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.9 '@babel/types': 7.29.8 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -11598,10 +11472,6 @@ snapshots: '@babel/template': 7.29.7 '@babel/types': 7.29.8 - '@babel/parser@7.29.7': - dependencies: - '@babel/types': 7.29.7 - '@babel/parser@7.29.9': dependencies: '@babel/types': 7.29.8 @@ -11611,7 +11481,7 @@ snapshots: '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.9 '@babel/types': 7.29.8 '@babel/traverse@7.29.7': @@ -11619,18 +11489,13 @@ snapshots: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.9 '@babel/template': 7.29.7 '@babel/types': 7.29.8 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.29.7': - dependencies: - '@babel/helper-string-parser': 7.29.7 - '@babel/helper-validator-identifier': 7.29.7 - '@babel/types@7.29.8': dependencies: '@babel/helper-string-parser': 7.29.7 @@ -11676,9 +11541,6 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@blazediff/core@1.9.1': - optional: true - '@borewit/text-codec@0.2.2': {} '@cap.js/server@2.0.0': @@ -11891,15 +11753,14 @@ snapshots: type-fest: 4.41.0 zod: link:vendor/zod@3.x - '@douglasneuroinformatics/libnest@9.0.0(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(@nestjs/platform-fastify@11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6))(@nestjs/testing@11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(@nestjs/platform-express@11.2.6))(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3))(@swc/types@0.1.28)(fastify@5.12.5)(neverthrow@8.2.0)(reflect-metadata@0.1.14)(rollup@4.61.1)(rxjs@7.8.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))(vitest@4.1.11)(zod@vendor+zod@3.x)': + '@douglasneuroinformatics/libnest@10.0.0(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-fastify@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@nestjs/testing@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3))(@swc/types@0.1.28)(fastify@5.12.5)(neverthrow@8.2.0)(reflect-metadata@0.2.2)(rollup@4.61.1)(rxjs@7.8.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))(vitest@5.0.2)(zod@vendor+zod@3.x)': dependencies: '@douglasneuroinformatics/libjs': 3.2.1(neverthrow@8.2.0)(zod@vendor+zod@3.x) - '@nestjs/common': 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@11.2.6)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/platform-fastify': 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6) - '@nestjs/swagger': 11.4.4(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(reflect-metadata@0.1.14) - '@nestjs/testing': 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(@nestjs/platform-express@11.2.6) - '@nestjs/throttler': 6.7.1(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(reflect-metadata@0.1.14) + '@nestjs/common': 12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/platform-fastify': 12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)) + '@nestjs/testing': 12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)) + '@nestjs/throttler': 6.7.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2) '@prisma/client': 6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3) '@prisma/engines': 6.19.3 '@prisma/get-platform': 6.19.3 @@ -11910,27 +11771,24 @@ snapshots: '@types/supertest': 6.0.3 chalk: 5.6.2 commander: 14.0.3 - es-module-lexer: 2.1.0 + es-module-lexer: 2.3.2 esbuild: 0.27.7 fastify: 5.12.5 neverthrow: 8.2.0 nodemailer: 7.0.13 - reflect-metadata: 0.1.14 + reflect-metadata: 0.2.2 rxjs: 7.8.2 serialize-error: 13.0.1 supertest: 7.2.2 ts-morph: 27.0.2 type-fest: 5.7.0 unplugin-swc: 1.5.9(@swc/core@1.16.2(@swc/helpers@0.5.23))(rollup@4.61.1) - vitest: 4.1.11(@types/node@24.19.0)(@vitest/coverage-v8@4.1.11)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + vitest: 5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) zod: link:vendor/zod@3.x optionalDependencies: vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) transitivePeerDependencies: - - '@fastify/static' - '@swc/types' - - class-transformer - - class-validator - rollup - supports-color - typescript @@ -12509,10 +12367,10 @@ snapshots: dependencies: fast-json-stringify: 6.4.0 - '@fastify/formbody@8.0.2': + '@fastify/formbody@9.0.0': dependencies: fast-querystring: 1.1.2 - fastify-plugin: 5.1.0 + fastify-plugin: 6.0.0 '@fastify/forwarded@3.0.1': {} @@ -12730,6 +12588,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -12887,8 +12747,6 @@ snapshots: jju: 1.4.0 resolve: 1.22.12 - '@microsoft/tsdoc@0.16.0': {} - '@microsoft/tsdoc@0.17.0': {} '@monaco-editor/loader@1.7.0': @@ -12945,72 +12803,54 @@ snapshots: '@tybys/wasm-util': 0.10.4 optional: true - '@nestjs/axios@12.0.1(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(axios@1.20.0)(rxjs@7.8.2)': + '@nestjs/axios@12.0.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.20.0)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2) axios: 1.20.0 rxjs: 7.8.2 - '@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2)': + '@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - file-type: 21.3.4 + '@standard-schema/spec': 1.1.0 + file-type: 22.1.1 iterare: 1.2.1 load-esm: 1.0.3 - reflect-metadata: 0.1.14 + reflect-metadata: 0.2.2 rxjs: 7.8.2 tslib: 2.8.1 uid: 2.0.2 transitivePeerDependencies: - supports-color - '@nestjs/core@11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@11.2.6)(reflect-metadata@0.1.14)(rxjs@7.8.2)': + '@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2) fast-safe-stringify: 2.1.1 iterare: 1.2.1 path-to-regexp: 8.4.2 - reflect-metadata: 0.1.14 + reflect-metadata: 0.2.2 rxjs: 7.8.2 tslib: 2.8.1 uid: 2.0.2 - optionalDependencies: - '@nestjs/platform-express': 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6) - '@nestjs/jwt@12.0.2(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))': + '@nestjs/jwt@12.0.2(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))': dependencies: - '@nestjs/common': 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2) '@types/jsonwebtoken': 9.0.10 jsonwebtoken: 9.0.3 - '@nestjs/mapped-types@2.1.1(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(reflect-metadata@0.1.14)': - dependencies: - '@nestjs/common': 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) - reflect-metadata: 0.1.14 - - '@nestjs/passport@12.0.0(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(passport@0.7.0)': + '@nestjs/passport@12.0.0(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)': dependencies: - '@nestjs/common': 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2) passport: 0.7.0 - '@nestjs/platform-express@11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)': - dependencies: - '@nestjs/common': 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@11.2.6)(reflect-metadata@0.1.14)(rxjs@7.8.2) - cors: 2.8.6 - express: 5.2.1 - multer: 2.4.0 - path-to-regexp: 8.4.2 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@nestjs/platform-fastify@11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)': + '@nestjs/platform-fastify@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))': dependencies: '@fastify/cors': 11.3.0 - '@fastify/formbody': 8.0.2 + '@fastify/formbody': 9.0.0 '@fastify/middie': 9.3.4 - '@nestjs/common': 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@11.2.6)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2) fast-querystring: 1.1.2 fastify: 5.12.5 find-my-way: 9.7.0 @@ -13018,31 +12858,17 @@ snapshots: path-to-regexp: 8.4.2 tslib: 2.8.1 - '@nestjs/swagger@11.4.4(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(reflect-metadata@0.1.14)': + '@nestjs/testing@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))': dependencies: - '@microsoft/tsdoc': 0.16.0 - '@nestjs/common': 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@11.2.6)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/mapped-types': 2.1.1(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(reflect-metadata@0.1.14) - js-yaml: 4.1.1 - lodash: 4.18.1 - path-to-regexp: 8.4.2 - reflect-metadata: 0.1.14 - swagger-ui-dist: 5.32.6 - - '@nestjs/testing@11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(@nestjs/platform-express@11.2.6)': - dependencies: - '@nestjs/common': 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@11.2.6)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 - optionalDependencies: - '@nestjs/platform-express': 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6) - '@nestjs/throttler@6.7.1(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@11.2.6)(reflect-metadata@0.1.14)': + '@nestjs/throttler@6.7.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)': dependencies: - '@nestjs/common': 11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 11.2.6(@nestjs/common@11.2.6(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@11.2.6)(reflect-metadata@0.1.14)(rxjs@7.8.2) - reflect-metadata: 0.1.14 + '@nestjs/common': 12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2) + reflect-metadata: 0.2.2 '@noble/hashes@1.8.0': {} @@ -14600,7 +14426,7 @@ snapshots: dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 - picomatch: 4.0.4 + picomatch: 4.0.7 optionalDependencies: rollup: 4.61.1 @@ -14720,8 +14546,6 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@scarf/scarf@1.4.0': {} - '@shikijs/core@3.23.0': dependencies: '@shikijs/types': 3.23.0 @@ -15063,7 +14887,7 @@ snapshots: colorette: 2.0.20 debug: 4.4.3 fast-json-stable-stringify: 2.1.0 - oxc-resolver: 11.20.0 + oxc-resolver: 11.24.2 pirates: 4.0.7 tslib: 2.8.1 typescript: 6.0.3 @@ -15449,24 +15273,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@types/body-parser@1.19.6': dependencies: @@ -15625,7 +15449,7 @@ snapshots: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 '@types/node': 24.19.0 - form-data: 4.0.5 + form-data: 4.0.6 '@types/supertest@6.0.3': dependencies: @@ -15768,39 +15592,17 @@ snapshots: transitivePeerDependencies: - '@swc/helpers' - '@vitest/browser@4.1.11(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))(vitest@4.1.11)': - dependencies: - '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.11(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) - '@vitest/utils': 4.1.11 - magic-string: 0.30.21 - pngjs: 7.0.0 - sirv: 3.0.2 - tinyrainbow: 3.1.0 - vitest: 4.1.11(@types/node@24.19.0)(@vitest/coverage-v8@4.1.11)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) - ws: 8.22.0 - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - optional: true - - '@vitest/coverage-v8@4.1.11(@vitest/browser@4.1.11)(vitest@4.1.11)': + '@vitest/coverage-v8@5.0.2(vitest@5.0.2)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.11 - ast-v8-to-istanbul: 1.0.3 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-reports: 3.2.0 - magicast: 0.5.3 - obug: 2.1.2 - std-env: 4.1.0 - tinyrainbow: 3.1.0 - vitest: 4.1.11(@types/node@24.19.0)(@vitest/coverage-v8@4.1.11)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) - optionalDependencies: - '@vitest/browser': 4.1.11(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))(vitest@4.1.11) + '@vitest/istanbul-lib-coverage': 1.0.2 + '@vitest/istanbul-lib-report': 1.0.2 + ast-v8-to-istanbul: 1.0.7 + magicast: 0.5.5 + obug: 2.2.1 + std-env: 4.3.0 + tinyrainbow: 3.1.1 + vitest: 5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) '@vitest/expect@3.2.4': dependencies: @@ -15810,20 +15612,18 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/expect@4.1.11': + '@vitest/istanbul-lib-coverage@1.0.2': {} + + '@vitest/istanbul-lib-report@1.0.2': dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 - chai: 6.2.2 - tinyrainbow: 3.1.0 + '@vitest/istanbul-lib-coverage': 1.0.2 - '@vitest/mocker@4.1.11(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@vitest/mocker@5.0.2(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.11 + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.2 estree-walker: 3.0.3 - magic-string: 0.30.21 + magic-string: 1.4.2 optionalDependencies: vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) @@ -15831,27 +15631,11 @@ snapshots: dependencies: tinyrainbow: 2.0.0 - '@vitest/pretty-format@4.1.11': - dependencies: - tinyrainbow: 3.1.0 - - '@vitest/runner@4.1.11': - dependencies: - '@vitest/utils': 4.1.11 - pathe: 2.0.3 - - '@vitest/snapshot@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - '@vitest/utils': 4.1.11 - magic-string: 0.30.21 - pathe: 2.0.3 - '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.4 - '@vitest/spy@4.1.11': {} + '@vitest/spy@5.0.2': {} '@vitest/utils@3.2.4': dependencies: @@ -15859,12 +15643,6 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 - '@vitest/utils@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 - '@volar/kit@2.4.28(typescript@6.0.3)': dependencies: '@volar/language-service': 2.4.28 @@ -16002,8 +15780,6 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.2 - append-field@1.0.0: {} - are-docs-informative@0.0.2: {} arg@5.0.2: {} @@ -16105,7 +15881,7 @@ snapshots: dependencies: tslib: 2.8.1 - ast-v8-to-istanbul@1.0.3: + ast-v8-to-istanbul@1.0.7: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 @@ -16412,10 +16188,6 @@ snapshots: dependencies: run-applescript: 7.1.0 - busboy@1.6.0: - dependencies: - streamsearch: 1.1.0 - bytes@3.1.2: {} c12@3.1.0: @@ -16644,11 +16416,6 @@ snapshots: core-util-is@1.0.3: {} - cors@2.8.6: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - cosmiconfig-typescript-loader@6.3.0(@types/node@24.19.0)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3): dependencies: '@types/node': 24.19.0 @@ -17051,8 +16818,6 @@ snapshots: es-module-lexer@1.7.0: {} - es-module-lexer@2.1.0: {} - es-module-lexer@2.3.2: {} es-object-atoms@1.1.2: @@ -17596,8 +17361,6 @@ snapshots: fastest-levenshtein@1.0.16: {} - fastify-plugin@5.1.0: {} - fastify-plugin@6.0.0: {} fastify@5.12.5: @@ -17644,7 +17407,7 @@ snapshots: file-selector@5.0.1: {} - file-type@21.3.4: + file-type@22.1.1: dependencies: '@tokenizer/inflate': 0.4.1 strtok3: 10.3.5 @@ -17720,14 +17483,6 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.4 - mime-types: 2.1.35 - form-data@4.0.6: dependencies: asynckit: 0.4.0 @@ -18145,8 +17900,6 @@ snapshots: hibp@15.2.1: {} - html-escaper@2.0.2: {} - html-escaper@3.0.3: {} html-void-elements@3.0.0: {} @@ -18391,19 +18144,6 @@ snapshots: isexe@2.0.0: {} - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-reports@3.2.0: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - iterare@1.2.1: {} iterator.prototype@1.1.5: @@ -18437,10 +18177,6 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.1.1: - dependencies: - argparse: 2.0.1 - js-yaml@4.3.2: dependencies: argparse: 2.0.1 @@ -18715,20 +18451,26 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@1.4.2: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + magicast@0.5.3: dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + source-map-js: 1.2.1 + + magicast@0.5.5: + dependencies: + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 source-map-js: 1.2.1 make-dir@3.1.0: dependencies: semver: 6.3.1 - make-dir@4.0.0: - dependencies: - semver: 7.8.1 - markdown-extensions@2.0.0: {} markdown-it@14.2.0: @@ -18935,8 +18677,6 @@ snapshots: mdurl@2.0.0: {} - media-typer@0.3.0: {} - media-typer@1.1.0: {} memory-pager@1.5.0: {} @@ -19392,12 +19132,6 @@ snapshots: muggle-string@0.4.1: {} - multer@2.4.0: - dependencies: - append-field: 1.0.0 - busboy: 1.6.0 - type-is: 1.6.18 - nanoid@3.3.19: {} natural-compare@1.4.0: {} @@ -19506,7 +19240,7 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.2 - obug@2.1.2: {} + obug@2.2.1: {} ofetch@1.5.1: dependencies: @@ -19837,9 +19571,6 @@ snapshots: pngjs@5.0.0: {} - pngjs@7.0.0: - optional: true - possible-typed-array-names@1.1.0: {} postcss-load-config@3.1.4(postcss@8.5.28): @@ -20005,7 +19736,7 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.28.0 '@types/doctrine': 0.0.9 @@ -20226,7 +19957,7 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 - reflect-metadata@0.1.14: {} + reflect-metadata@0.2.2: {} reflect.getprototypeof@1.0.10: dependencies: @@ -20688,8 +20419,6 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - siginfo@2.0.0: {} - signal-exit@4.1.0: {} simple-statistics@7.8.3: {} @@ -20790,8 +20519,6 @@ snapshots: dependencies: frac: 1.1.2 - stackback@0.0.2: {} - stacktrace-parser@0.1.11: dependencies: type-fest: 0.7.1 @@ -20800,7 +20527,7 @@ snapshots: statuses@2.0.2: {} - std-env@4.1.0: {} + std-env@4.3.0: {} stop-iteration-iterator@1.1.0: dependencies: @@ -20861,8 +20588,6 @@ snapshots: - react-dom - utf-8-validate - streamsearch@1.1.0: {} - streamx@2.26.0: dependencies: events-universal: 1.0.1 @@ -20998,7 +20723,7 @@ snapshots: cookiejar: 2.1.4 debug: 4.4.3 fast-safe-stringify: 2.1.1 - form-data: 4.0.5 + form-data: 4.0.6 formidable: 3.5.4 methods: 1.1.2 mime: 2.6.0 @@ -21046,10 +20771,6 @@ snapshots: picocolors: 1.1.1 sax: 1.6.0 - swagger-ui-dist@5.32.6: - dependencies: - '@scarf/scarf': 1.4.0 - synckit@0.11.13: dependencies: '@pkgr/core': 0.3.6 @@ -21100,18 +20821,20 @@ snapshots: tiny-invariant@1.3.3: {} - tinybench@2.9.0: {} + tinybench@6.2.0: {} tinyexec@1.2.4: {} + tinyexec@1.3.1: {} + tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 tinyrainbow@2.0.0: {} - tinyrainbow@3.1.0: {} + tinyrainbow@3.1.1: {} tinyspy@4.0.4: {} @@ -21206,11 +20929,6 @@ snapshots: dependencies: tagged-tag: 1.0.0 - type-is@1.6.18: - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 - type-is@2.1.0: dependencies: content-type: 2.0.0 @@ -21393,7 +21111,7 @@ snapshots: dependencies: '@jridgewell/remapping': 2.3.5 acorn: 8.16.0 - picomatch: 4.0.4 + picomatch: 4.0.7 webpack-virtual-modules: 0.6.2 unplugin@3.4.0(esbuild@0.28.2)(rollup@4.61.1)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)): @@ -21531,8 +21249,8 @@ snapshots: vite@6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0): dependencies: esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 postcss: 8.5.28 rollup: 4.61.1 tinyglobby: 0.2.17 @@ -21564,31 +21282,25 @@ snapshots: optionalDependencies: vite: 6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) - vitest@4.1.11(@types/node@24.19.0)(@vitest/coverage-v8@4.1.11)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)): + vitest@5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)): dependencies: - '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.11 - '@vitest/runner': 4.1.11 - '@vitest/snapshot': 4.1.11 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.2(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + chai: 6.2.2 es-module-lexer: 2.3.2 expect-type: 1.4.0 - magic-string: 0.30.21 - obug: 2.1.2 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.2.4 + magic-string: 1.4.2 + obug: 2.2.1 + picomatch: 4.0.7 + std-env: 4.3.0 + tinybench: 6.2.0 + tinyexec: 1.3.1 tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) - why-is-node-running: 2.3.0 + why-is-node-running: 3.2.2 optionalDependencies: '@types/node': 24.19.0 - '@vitest/coverage-v8': 4.1.11(@vitest/browser@4.1.11)(vitest@4.1.11) + '@vitest/coverage-v8': 5.0.2(vitest@5.0.2) happy-dom: 20.11.15 transitivePeerDependencies: - msw @@ -21763,10 +21475,7 @@ snapshots: dependencies: isexe: 2.0.0 - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 + why-is-node-running@3.2.2: {} widest-line@5.0.0: dependencies: diff --git a/runtime/v1/vitest.config.ts b/runtime/v1/vitest.config.ts index 0f3b3f888..9b4fd729a 100644 --- a/runtime/v1/vitest.config.ts +++ b/runtime/v1/vitest.config.ts @@ -1,6 +1,6 @@ import { defineProject, mergeConfig } from 'vitest/config'; -import baseConfig from '../../vitest.config'; +import { baseConfig } from '../../vitest.config'; export default mergeConfig( baseConfig, diff --git a/vitest.config.ts b/vitest.config.ts index 3a36a639a..875df0e93 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,37 +1,47 @@ import path from 'path'; -import { defineConfig } from 'vitest/config'; +import { defineConfig, mergeConfig } from 'vitest/config'; -export default defineConfig({ +// Project configs merge this rather than the default export, because Vitest 5 treats a `projects` +// field inside a referenced project config as a nested set of projects. +export const baseConfig = defineConfig({ test: { - coverage: { - exclude: [ - '**/coverage/**', - '**/dist/**', - '**/node_modules/**', - '**/public/**', - '**/scripts/**', - '**/*.d.?(c|m)ts', - '**/*{.,-}{test,test-d,spec}.?(c|m)[jt]s?(x)', - '**/*.config.?(c|m)[jt]s?(x)', - '**/*.stories.?(c|m)[jt]s?(x)', - '**/__tests__/**', - 'apps/gateway/src/entry-client.tsx', - 'apps/gateway/src/main.ts', - 'apps/web/src/main.tsx', - 'apps/web/src/route-tree.ts', - 'apps/web/src/testing/**' - ], - include: ['apps/{api,gateway,web}/src/**/*.?(c|m)[jt]s?(x)', 'packages/*/src/**/*.?(c|m)[jt]s?(x)'], - provider: 'v8', - reportsDirectory: path.resolve(import.meta.dirname, 'coverage'), - skipFull: true, - thresholds: { - 100: true - } - }, include: ['**/*.{test,spec}.?(c|m)[jt]s?(x)'], - projects: ['apps/*/vitest.config.ts', 'packages/*/vitest.config.ts', 'runtime/*/vitest.config.ts'], watch: false } }); + +export default mergeConfig( + baseConfig, + defineConfig({ + test: { + coverage: { + exclude: [ + '**/coverage/**', + '**/dist/**', + '**/node_modules/**', + '**/public/**', + '**/scripts/**', + '**/*.d.?(c|m)ts', + '**/*{.,-}{test,test-d,spec}.?(c|m)[jt]s?(x)', + '**/*.config.?(c|m)[jt]s?(x)', + '**/*.stories.?(c|m)[jt]s?(x)', + '**/__tests__/**', + 'apps/gateway/src/entry-client.tsx', + 'apps/gateway/src/main.ts', + 'apps/web/src/main.tsx', + 'apps/web/src/route-tree.ts', + 'apps/web/src/testing/**' + ], + include: ['apps/{api,gateway,web}/src/**/*.?(c|m)[jt]s?(x)', 'packages/*/src/**/*.?(c|m)[jt]s?(x)'], + provider: 'v8', + reportsDirectory: path.resolve(import.meta.dirname, 'coverage'), + skipFull: true, + thresholds: { + 100: true + } + }, + projects: ['apps/*/vitest.config.ts', 'packages/*/vitest.config.ts', 'runtime/*/vitest.config.ts'] + } + }) +); From d2c1b933a691727b5e3e574414f573ccb1dbae05 Mon Sep 17 00:00:00 2001 From: joshunrau Date: Tue, 6 Oct 2026 17:44:56 -0400 Subject: [PATCH 02/14] build: upgrade js-yaml to 5 js-yaml 5 drops the default export and only has named exports, so commitlint.config.ts now imports `load` by name. Without that change the config fails to load, which breaks the commit-msg hook and the Commitlint CI job. js-yaml 5 ships its own type declarations, so @types/js-yaml is removed from the root devDependencies. js-yaml 4.3.2 stays in the lockfile only as a transitive dependency of eslint, cosmiconfig and astro, whose ranges are still ^4. Co-Authored-By: Claude Opus 5.5 --- commitlint.config.ts | 4 ++-- package.json | 3 +-- pnpm-lock.yaml | 15 ++++++++++----- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/commitlint.config.ts b/commitlint.config.ts index cc32186c4..f91ea34c8 100644 --- a/commitlint.config.ts +++ b/commitlint.config.ts @@ -3,14 +3,14 @@ import fs from 'node:fs'; import path from 'node:path'; -import yaml from 'js-yaml'; +import { load } from 'js-yaml'; const WORKSPACE_NAME_PREFIX = '@opendatacapture/'; const projectRoot = import.meta.dirname; function readWorkspaceGlobs(): string[] { - const workspace: unknown = yaml.load(fs.readFileSync(path.join(projectRoot, 'pnpm-workspace.yaml'), 'utf8')); + const workspace: unknown = load(fs.readFileSync(path.join(projectRoot, 'pnpm-workspace.yaml'), 'utf8')); if (typeof workspace !== 'object' || workspace === null || !('packages' in workspace)) { throw new Error("pnpm-workspace.yaml has no 'packages' field"); } diff --git a/package.json b/package.json index effc67bc0..b55d42302 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "@douglasneuroinformatics/eslint-config": "^6.0.0", "@douglasneuroinformatics/prettier-config": "^0.3.0", "@douglasneuroinformatics/tsconfig": "^2.0.0", - "@types/js-yaml": "^4.0.9", "@types/node": "24.x", "@vitest/coverage-v8": "^5.0.2", "conventional-commits-parser": "catalog:", @@ -46,7 +45,7 @@ "eslint": "9.39.x", "expect-type": "^1.4.0", "husky": "^9.1.7", - "js-yaml": "^4.3.2", + "js-yaml": "^5.4.2", "knip": "^6.38.0", "prettier": "^3.9.9", "prettier-plugin-astro": "^0.14.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2fe8f39e..1f8b9f91a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -151,9 +151,6 @@ importers: '@douglasneuroinformatics/tsconfig': specifier: ^2.0.0 version: 2.0.0(typescript@6.0.3) - '@types/js-yaml': - specifier: ^4.0.9 - version: 4.0.9 '@types/node': specifier: 24.x version: 24.19.0 @@ -176,8 +173,8 @@ importers: specifier: ^9.1.7 version: 9.1.7 js-yaml: - specifier: ^4.3.2 - version: 4.3.2 + specifier: ^5.4.2 + version: 5.4.2 knip: specifier: ^6.38.0 version: 6.38.0 @@ -8095,6 +8092,10 @@ packages: resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} hasBin: true + js-yaml@5.4.2: + resolution: {integrity: sha512-m+aqu+LwO1O6sIopafj8HUVl5aawITwZQe/yHpMCKjaWBaA/d07B/QdMb3529REftiU+RMMHL3Vlsw3hON7vWg==} + hasBin: true + jsdoc-type-pratt-parser@4.1.0: resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} engines: {node: '>=12.0.0'} @@ -18181,6 +18182,10 @@ snapshots: dependencies: argparse: 2.0.1 + js-yaml@5.4.2: + dependencies: + argparse: 2.0.1 + jsdoc-type-pratt-parser@4.1.0: {} jsesc@3.1.0: {} From 6b0ad61a84932e82e3c44fd51162b4b3f1a21bc4 Mon Sep 17 00:00:00 2001 From: joshunrau Date: Tue, 6 Oct 2026 18:04:36 -0400 Subject: [PATCH 03/14] build: upgrade prettier-config to 0.4.0 0.4.0 widens the prettier-plugin-astro peer range to 0.x || 1.x and rewrites the prettier-pre-commit hook to read staged paths NUL-separated (git diff -z, xargs -0), so filenames containing spaces or quotes are formatted and re-staged correctly. The createConfig factory is unchanged. prettier-plugin-astro deliberately stays on 0.14.1. With plugin 1.x, prettier-plugin-tailwindcss 0.8.1 stops sorting Tailwind classes in .astro files, so the root pin holds until a stable Tailwind plugin release fixes that (#1629). Co-Authored-By: Claude Opus 5.5 --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index b55d42302..846463300 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "@commitlint/cli": "catalog:", "@commitlint/config-conventional": "catalog:", "@douglasneuroinformatics/eslint-config": "^6.0.0", - "@douglasneuroinformatics/prettier-config": "^0.3.0", + "@douglasneuroinformatics/prettier-config": "^0.4.0", "@douglasneuroinformatics/tsconfig": "^2.0.0", "@types/node": "24.x", "@vitest/coverage-v8": "^5.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f8b9f91a..95cb5cab5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -146,8 +146,8 @@ importers: specifier: ^6.0.0 version: 6.0.0(@typescript-eslint/parser@8.60.1(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) '@douglasneuroinformatics/prettier-config': - specifier: ^0.3.0 - version: 0.3.0(husky@9.1.7)(prettier-plugin-astro@0.14.1)(prettier-plugin-tailwindcss@0.8.1(prettier-plugin-astro@0.14.1)(prettier@3.9.9))(prettier@3.9.9) + specifier: ^0.4.0 + version: 0.4.0(husky@9.1.7)(prettier-plugin-astro@0.14.1)(prettier-plugin-tailwindcss@0.8.1(prettier-plugin-astro@0.14.1)(prettier@3.9.9))(prettier@3.9.9) '@douglasneuroinformatics/tsconfig': specifier: ^2.0.0 version: 2.0.0(typescript@6.0.3) @@ -2246,13 +2246,13 @@ packages: tailwindcss: ^4.1.0 zod: ^3.25.x - '@douglasneuroinformatics/prettier-config@0.3.0': - resolution: {integrity: sha512-pa8AXwRQHmq99nbYsAfWjfk0MOT0sjidsfP0VMaWEvDQy4l055kLwggWa0aqc5C6V3ZubA56K4gx5+r3/drslQ==} + '@douglasneuroinformatics/prettier-config@0.4.0': + resolution: {integrity: sha512-uB8vV0ExwUPfxTxdTvyOmWOxIwIKt9KdTmUXwSErIbDb1nTJ1BWbb+jXSeWD22BjvBuSty8mC7B7SoB8PhPamA==} hasBin: true peerDependencies: husky: 9.x prettier: 3.x - prettier-plugin-astro: 0.x + prettier-plugin-astro: 0.x || 1.x prettier-plugin-svelte: 3.x prettier-plugin-tailwindcss: 0.x peerDependenciesMeta: @@ -11956,7 +11956,7 @@ snapshots: - neverthrow - use-sync-external-store - '@douglasneuroinformatics/prettier-config@0.3.0(husky@9.1.7)(prettier-plugin-astro@0.14.1)(prettier-plugin-tailwindcss@0.8.1(prettier-plugin-astro@0.14.1)(prettier@3.9.9))(prettier@3.9.9)': + '@douglasneuroinformatics/prettier-config@0.4.0(husky@9.1.7)(prettier-plugin-astro@0.14.1)(prettier-plugin-tailwindcss@0.8.1(prettier-plugin-astro@0.14.1)(prettier@3.9.9))(prettier@3.9.9)': dependencies: prettier: 3.9.9 optionalDependencies: From c0d58b95d9a2fe19ad566f5f41000590713bca15 Mon Sep 17 00:00:00 2001 From: joshunrau Date: Tue, 6 Oct 2026 21:54:14 -0400 Subject: [PATCH 04/14] build(api): upgrade faker to 10.6 apps/api moves @faker-js/faker from ~10.2.0 to ^10.6.0, back on a caret range (#1620). It is declared nowhere else, and the lockfile holds one copy. faker 10.3 adds 'generic' to its Sex enum, so person.sexType() is typed 'female' | 'male' | 'generic' and the demo seeder's upper-cased value no longer fits $Sex. The seeder now draws a dummy subject's sex from $Sex.options, so it offers exactly the sexes the subject schema accepts and cannot drift from it. The demo service test pins that the seeded subjects cover every $Sex value and nothing else. No other faker call the seeder makes changed its signature, and no unit or e2e test asserts on specific seeded values. Co-Authored-By: Claude Opus 5.5 --- apps/api/package.json | 2 +- apps/api/src/demo/__tests__/demo.service.test.ts | 7 +++++++ apps/api/src/demo/demo.service.ts | 5 +++-- pnpm-lock.yaml | 10 +++++----- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/apps/api/package.json b/apps/api/package.json index 83e29425f..2b15688ef 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -24,7 +24,7 @@ "@douglasneuroinformatics/libnest": "^10.0.0", "@douglasneuroinformatics/libpasswd": "catalog:", "@douglasneuroinformatics/libstats": "catalog:", - "@faker-js/faker": "~10.2.0", + "@faker-js/faker": "^10.6.0", "@nestjs/axios": "^12.0.1", "@nestjs/common": "^12.1.1", "@nestjs/core": "^12.1.1", diff --git a/apps/api/src/demo/__tests__/demo.service.test.ts b/apps/api/src/demo/__tests__/demo.service.test.ts index 4676915e4..d494f0c2b 100644 --- a/apps/api/src/demo/__tests__/demo.service.test.ts +++ b/apps/api/src/demo/__tests__/demo.service.test.ts @@ -6,6 +6,7 @@ import { InternalServerErrorException } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { DEMO_GROUPS, DEMO_USERS } from '@opendatacapture/demo'; import type { $CreateGroupData } from '@opendatacapture/schemas/group'; +import { $Sex } from '@opendatacapture/schemas/subject'; import { encodeScopedSubjectId, generateSubjectHash } from '@opendatacapture/subject-utils'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -149,6 +150,12 @@ describe('DemoService', () => { expect(subject.id).toBe(await generateSubjectHash({ dateOfBirth, firstName, lastName, sex })); }); + it('should draw dummy subjects from every sex the subject schema accepts and no other', async () => { + await demoService.init({ dummySubjectCount: 20, recordsPerSubject: 0 }); + const sexes = subjectsService.create.mock.calls.map(([subject]) => subject.sex); + expect(new Set(sexes)).toStrictEqual(new Set($Sex.options)); + }); + it('should number dummy subjects in a custom-id group in sequence under the group prefix', async () => { vi.spyOn(Math, 'random').mockReturnValue(LAST_GROUP); await demoService.init({ dummySubjectCount: 2, recordsPerSubject: 0 }); diff --git a/apps/api/src/demo/demo.service.ts b/apps/api/src/demo/demo.service.ts index dbea5f795..01e6cb00c 100644 --- a/apps/api/src/demo/demo.service.ts +++ b/apps/api/src/demo/demo.service.ts @@ -1,4 +1,4 @@ -import { randomValue, toUpperCase } from '@douglasneuroinformatics/libjs'; +import { randomValue } from '@douglasneuroinformatics/libjs'; import { InjectPrismaClient, LoggingService } from '@douglasneuroinformatics/libnest'; import { faker } from '@faker-js/faker'; import { Injectable, InternalServerErrorException } from '@nestjs/common'; @@ -14,6 +14,7 @@ import happinessQuestionnaireWithConsent from '@opendatacapture/instrument-libra import type { FormInstrument, Language } from '@opendatacapture/runtime-core'; import type { WithID } from '@opendatacapture/schemas/core'; import type { Group } from '@opendatacapture/schemas/group'; +import { $Sex } from '@opendatacapture/schemas/subject'; import { encodeScopedSubjectId, generateSubjectHash } from '@opendatacapture/subject-utils'; import type { RuntimePrismaClient } from '@/core/prisma'; @@ -106,7 +107,7 @@ export class DemoService { dateOfBirth: faker.date.birthdate(), firstName: faker.person.firstName(), lastName: faker.person.lastName(), - sex: toUpperCase(faker.person.sexType()) + sex: faker.helpers.arrayElement($Sex.options) }; let subjectId: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95cb5cab5..46bdb0317 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -233,8 +233,8 @@ importers: specifier: 'catalog:' version: 0.2.1 '@faker-js/faker': - specifier: ~10.2.0 - version: 10.2.0 + specifier: ^10.6.0 + version: 10.6.0 '@nestjs/axios': specifier: ^12.0.1 version: 12.0.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.20.0)(rxjs@7.8.2) @@ -2841,8 +2841,8 @@ packages: '@expressive-code/plugin-text-markers@0.41.7': resolution: {integrity: sha512-Ewpwuc5t6eFdZmWlFyeuy3e1PTQC0jFvw2Q+2bpcWXbOZhPLsT7+h8lsSIJxb5mS7wZko7cKyQ2RLYDyK6Fpmw==} - '@faker-js/faker@10.2.0': - resolution: {integrity: sha512-rTXwAsIxpCqzUnZvrxVh3L0QA0NzToqWBLAhV+zDV3MIIwiQhAZHMdPCIaj5n/yADu/tyk12wIPgL6YHGXJP+g==} + '@faker-js/faker@10.6.0': + resolution: {integrity: sha512-3RQHgEtvL1Frl/d1cSreo7qhJ3Gk1OdNUai/CtZ8G+wYeRQnJih3s9xJ9/kgYekPQRdwgh0HXRPqMlzWGwivIQ==} engines: {node: ^20.19.0 || ^22.13.0 || ^23.5.0 || >=24.0.0, npm: '>=10'} '@fastify/ajv-compiler@4.0.5': @@ -12349,7 +12349,7 @@ snapshots: dependencies: '@expressive-code/core': 0.41.7 - '@faker-js/faker@10.2.0': {} + '@faker-js/faker@10.6.0': {} '@fastify/ajv-compiler@4.0.5': dependencies: From f49823d48209828d95f649afc2392f835dd1ee1c Mon Sep 17 00:00:00 2001 From: joshunrau Date: Tue, 6 Oct 2026 22:06:17 -0400 Subject: [PATCH 05/14] build(gateway): upgrade @cap.js/server to 4 apps/gateway moves @cap.js/server from ^2.0.0 to ^4.0.5 (#1612), and the lockfile holds one copy. From 3.0.0, Cap#createChallenge returns a promise. The /challenge handler passed its return value straight to res.json, so on 4.x it would have answered with an empty object and the widget could never solve a challenge. Neither tsc nor the router test caught it, because res.json accepts anything and the test mocked createChallenge as synchronous. The handler now awaits the challenge and is wrapped in ah like the other async routes. The router test types its Cap mocks with the library's own method signatures, so a synchronous createChallenge mock is now a type error, and it asserts that the response is the resolved challenge rather than the pending promise. Nothing else this code relies on changed between 2.0.0 and 4.0.5: the createChallenge options, redeemChallenge, validateToken, the Solution type, the noFSState option, and the { challenge: { c, s, d }, token, expires } response that @cap.js/widget 0.1.58 solves. 4.0.5 also drops the runtime dependencies on typescript and @types/node that 2.0.0 declared. Co-Authored-By: Claude Opus 5.5 --- apps/gateway/package.json | 2 +- .../src/routers/__tests__/cap.router.test.ts | 20 ++++++++++----- apps/gateway/src/routers/cap.router.ts | 19 ++++++++------ pnpm-lock.yaml | 25 ++++--------------- 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/apps/gateway/package.json b/apps/gateway/package.json index 96e9f8ccc..e640d51f0 100644 --- a/apps/gateway/package.json +++ b/apps/gateway/package.json @@ -18,7 +18,7 @@ "start": "NODE_ENV=production env-cmd -f ../../.env node dist/main.js" }, "dependencies": { - "@cap.js/server": "^2.0.0", + "@cap.js/server": "^4.0.5", "@cap.js/widget": "^0.1.58", "@douglasneuroinformatics/libcrypto": "catalog:", "@douglasneuroinformatics/libui": "catalog:", diff --git a/apps/gateway/src/routers/__tests__/cap.router.test.ts b/apps/gateway/src/routers/__tests__/cap.router.test.ts index 293ec4a91..5c38e3190 100644 --- a/apps/gateway/src/routers/__tests__/cap.router.test.ts +++ b/apps/gateway/src/routers/__tests__/cap.router.test.ts @@ -1,5 +1,6 @@ import type { Server } from 'http'; +import type Cap from '@cap.js/server'; import express from 'express'; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -10,12 +11,14 @@ import { $VerifyRequest, capRouter } from '../cap.router'; const { cap } = vi.hoisted(() => ({ cap: { - createChallenge: vi.fn(), - redeemChallenge: vi.fn(), - validateToken: vi.fn() + createChallenge: vi.fn(), + redeemChallenge: vi.fn(), + validateToken: vi.fn() } })); +const challenge = { challenge: { c: 1, d: 4, s: 2 }, expires: 1_000, token: 'challenge-token' }; + vi.mock('@/lib/cap', () => ({ cap })); vi.mock('@/logger', () => ({ logger: { error: vi.fn() } })); @@ -64,11 +67,16 @@ describe('$VerifyRequest', () => { }); describe('POST /challenge', () => { - it('should issue a challenge that expires in five minutes, so a stale challenge cannot be solved later', async () => { - cap.createChallenge.mockReturnValueOnce({ challenge: { c: 1, d: 4, s: 2 }, token: 'challenge-token' }); + it('should respond with the challenge Cap resolves, not the pending promise, so the widget has a challenge to solve', async () => { + cap.createChallenge.mockResolvedValueOnce(challenge); const response = await post('/challenge', {}); expect(response.status).toBe(200); - expect(await response.json()).toEqual({ challenge: { c: 1, d: 4, s: 2 }, token: 'challenge-token' }); + expect(await response.json()).toEqual(challenge); + }); + + it('should issue a challenge that expires in five minutes, so a stale challenge cannot be solved later', async () => { + cap.createChallenge.mockResolvedValueOnce(challenge); + await post('/challenge', {}); expect(cap.createChallenge).toHaveBeenCalledWith({ challengeDifficulty: 4, expiresMs: 300_000 }); }); }); diff --git a/apps/gateway/src/routers/cap.router.ts b/apps/gateway/src/routers/cap.router.ts index ec9034d96..e01cd58ff 100644 --- a/apps/gateway/src/routers/cap.router.ts +++ b/apps/gateway/src/routers/cap.router.ts @@ -25,14 +25,17 @@ const $VerifyRequest = z.object({ const router = Router(); -router.post('/challenge', (_, res) => { - res.status(200).json( - cap.createChallenge({ - challengeDifficulty: 4, - expiresMs: 60_000 * 5 - }) - ); -}); +router.post( + '/challenge', + ah(async (_, res) => { + res.status(200).json( + await cap.createChallenge({ + challengeDifficulty: 4, + expiresMs: 60_000 * 5 + }) + ); + }) +); router.post( '/redeem', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46bdb0317..4e690e5ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -357,8 +357,8 @@ importers: apps/gateway: dependencies: '@cap.js/server': - specifier: ^2.0.0 - version: 2.0.0 + specifier: ^4.0.5 + version: 4.0.5 '@cap.js/widget': specifier: ^0.1.58 version: 0.1.58 @@ -2003,8 +2003,8 @@ packages: '@borewit/text-codec@0.2.2': resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} - '@cap.js/server@2.0.0': - resolution: {integrity: sha512-CaETNf+u/+vgkzQJu3Fq3UWrd8egUFYBFLYo5Rf5j2muIVY1syqUJbwvwxLbgE5mlMmND/RUo15D/G0iKCOIYA==} + '@cap.js/server@4.0.5': + resolution: {integrity: sha512-S902YIyGyiriNaexfqTzMy5ixxSb3/jYSJwr2gAY9d7XXoYftcifgij/11pjuiU72Ogm6XKZOggDB2DtjRMyww==} '@cap.js/widget@0.1.58': resolution: {integrity: sha512-snNjlYZLx1XddDH7DPZF9uedynSXTPPeRl/nExDlMjw0pCHiu/Fm1NKjRcT4HmqpOBf0TZRvio1HhkTMWg9p9A==} @@ -5825,9 +5825,6 @@ packages: '@types/nlcst@2.0.3': resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} - '@types/node@22.19.21': - resolution: {integrity: sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA==} - '@types/node@24.19.0': resolution: {integrity: sha512-zY+5tKxXdhGh1PYI0ac+7juvEu4OI6vWtVVoj5i2m42jxAY1U+zHGt6QCyOFwykdP62sM3MJ9stoYYUw5aCWew==} @@ -10370,9 +10367,6 @@ packages: uncrypto@0.1.3: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} @@ -11544,10 +11538,7 @@ snapshots: '@borewit/text-codec@0.2.2': {} - '@cap.js/server@2.0.0': - dependencies: - '@types/node': 22.19.21 - typescript: 6.0.3 + '@cap.js/server@4.0.5': {} '@cap.js/widget@0.1.58': {} @@ -15396,10 +15387,6 @@ snapshots: dependencies: '@types/unist': 3.0.3 - '@types/node@22.19.21': - dependencies: - undici-types: 6.21.0 - '@types/node@24.19.0': dependencies: undici-types: 7.24.6 @@ -21028,8 +21015,6 @@ snapshots: uncrypto@0.1.3: {} - undici-types@6.21.0: {} - undici-types@7.24.6: {} unified@11.0.5: From 4532c78782a79bfaea4abaa2e316f5079c7b9fba Mon Sep 17 00:00:00 2001 From: joshunrau Date: Tue, 6 Oct 2026 22:28:09 -0400 Subject: [PATCH 06/14] build: upgrade lucide-react to 1 apps/web, apps/playground and packages/react-core move lucide-react from ^0.577.0 to ^1.49.0, the newest release past the 7-day release age (#1614). The lockfile now also resolves libui's ^1.7.0 range to 1.49.0 instead of 1.17.0, so it holds a single lucide-react version and the bundles stop carrying two copies. lucide 1.0 removed every brand icon, including the GithubIcon that the login branding panel showed beside its "Source Code" link. A new GitHubIcon component in apps/web draws GitHub's official mark instead, the 16px mark-github octicon from @primer/octicons 19.39.0 (MIT), rather than lucide's old outline drawing, which was a modified logo. It fills with currentColor, keeps the octicon's 16x16 viewBox, is aria-hidden like the lucide icons around it, and takes a className so the panel keeps its h-3 w-3 preview and h-4 w-4 full-size sizing. Its own test pins those attributes, and the panel test pins both sizes at the call site. Every other icon the three workspaces import still exists in 1.49.0. lucide 0.577.0 already set aria-hidden on icons given no aria-*, role or title prop, by the same rule 1.x uses, so no accessible name changes and no test or e2e selector finds an icon by role or name. Co-Authored-By: Claude Opus 5.5 --- apps/playground/package.json | 2 +- apps/web/package.json | 2 +- .../src/components/GitHubIcon/GitHubIcon.tsx | 17 +++++++++ .../GitHubIcon/__tests__/GitHubIcon.test.tsx | 26 +++++++++++++ apps/web/src/components/GitHubIcon/index.ts | 1 + .../LoginBranding/LoginBrandingPanel.tsx | 5 ++- .../__tests__/LoginBrandingPanel.test.tsx | 12 ++++++ packages/react-core/package.json | 2 +- pnpm-lock.yaml | 37 ++++++++----------- 9 files changed, 78 insertions(+), 26 deletions(-) create mode 100644 apps/web/src/components/GitHubIcon/GitHubIcon.tsx create mode 100644 apps/web/src/components/GitHubIcon/__tests__/GitHubIcon.test.tsx create mode 100644 apps/web/src/components/GitHubIcon/index.ts diff --git a/apps/playground/package.json b/apps/playground/package.json index 4aa64be0f..2757f0309 100644 --- a/apps/playground/package.json +++ b/apps/playground/package.json @@ -31,7 +31,7 @@ "jszip": "^3.10.2", "jwt-decode": "^4.0.0", "lodash-es": "workspace:lodash-es__4.x@*", - "lucide-react": "^0.577.0", + "lucide-react": "^1.49.0", "monaco-editor": "^0.54.0", "motion": "catalog:", "neverthrow": "catalog:", diff --git a/apps/web/package.json b/apps/web/package.json index c87f65aa3..2790dc486 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -41,7 +41,7 @@ "immer": "^11.1.18", "jwt-decode": "^4.0.0", "lodash-es": "workspace:lodash-es__4.x@*", - "lucide-react": "^0.577.0", + "lucide-react": "^1.49.0", "motion": "catalog:", "msgpackr": "catalog:", "papaparse": "workspace:papaparse__5.x@*", diff --git a/apps/web/src/components/GitHubIcon/GitHubIcon.tsx b/apps/web/src/components/GitHubIcon/GitHubIcon.tsx new file mode 100644 index 000000000..3b72b9363 --- /dev/null +++ b/apps/web/src/components/GitHubIcon/GitHubIcon.tsx @@ -0,0 +1,17 @@ +type GitHubIconProps = { + className?: string; +}; + +// GitHub's official mark: the 16px `mark-github` octicon from @primer/octicons 19.39.0 (MIT). lucide +// 1.0 removed every brand icon, and its old GitHub drawing was a modified logo. +export const GitHubIcon = ({ className }: GitHubIconProps) => ( + +); diff --git a/apps/web/src/components/GitHubIcon/__tests__/GitHubIcon.test.tsx b/apps/web/src/components/GitHubIcon/__tests__/GitHubIcon.test.tsx new file mode 100644 index 000000000..8f78d898a --- /dev/null +++ b/apps/web/src/components/GitHubIcon/__tests__/GitHubIcon.test.tsx @@ -0,0 +1,26 @@ +import { cleanup, render } from '@testing-library/react'; +import { afterEach, describe, expect, it } from 'vitest'; + +import { GitHubIcon } from '../GitHubIcon'; + +const renderMark = (className?: string) => render().container.querySelector('svg')!; + +describe('GitHubIcon', () => { + afterEach(cleanup); + + it('should be hidden from assistive technology, since the link text beside it already names the destination', () => { + expect(renderMark().getAttribute('aria-hidden')).toBe('true'); + }); + + it('should fill with the current text colour, so it matches the link it sits in', () => { + expect(renderMark().getAttribute('fill')).toBe('currentColor'); + }); + + it("should keep the octicon's own 16px viewBox, so the path is not cropped", () => { + expect(renderMark().getAttribute('viewBox')).toBe('0 0 16 16'); + }); + + it('should apply the caller class name, so the call site controls its size', () => { + expect(renderMark('h-4 w-4').getAttribute('class')).toBe('h-4 w-4'); + }); +}); diff --git a/apps/web/src/components/GitHubIcon/index.ts b/apps/web/src/components/GitHubIcon/index.ts new file mode 100644 index 000000000..161713953 --- /dev/null +++ b/apps/web/src/components/GitHubIcon/index.ts @@ -0,0 +1 @@ +export * from './GitHubIcon'; diff --git a/apps/web/src/components/LoginBranding/LoginBrandingPanel.tsx b/apps/web/src/components/LoginBranding/LoginBrandingPanel.tsx index 220735b3d..a82a615fb 100644 --- a/apps/web/src/components/LoginBranding/LoginBrandingPanel.tsx +++ b/apps/web/src/components/LoginBranding/LoginBrandingPanel.tsx @@ -12,8 +12,9 @@ import type { PanelSection, ResourceLink } from '@opendatacapture/schemas/setup'; -import { BookOpenIcon, GithubIcon, LinkIcon } from 'lucide-react'; +import { BookOpenIcon, LinkIcon } from 'lucide-react'; +import { GitHubIcon } from '@/components/GitHubIcon'; import { config } from '@/config'; import { getLoginGradient } from '@/utils/branding'; import { getValueForLanguage } from '@/utils/language'; @@ -191,7 +192,7 @@ const PanelFooter = ({ preview, showFooterLinks, tc, tl }: PanelFooterProps) => rel="noopener noreferrer" target="_blank" > - + {tl({ en: 'Source Code', es: 'Código fuente', fr: 'Code source' })} screen.getByTestId('login-branding-panel'); const heading = () => screen.getByRole('heading', { level: 1 }); const logoImage = () => screen.getByRole('img'); const builtInLogo = () => panel().querySelector('svg[viewBox="0 0 320 259"]'); +const sourceCodeMark = () => + screen.getByRole('link', { name: 'Source Code' }).querySelector('svg[viewBox="0 0 16 16"]')!; const sectionTexts = () => [...panel().children[1]!.children].map((section) => section.textContent); describe('LoginBrandingPanel', () => { @@ -340,6 +342,16 @@ describe('LoginBrandingPanel', () => { expect(screen.getByRole('link', { name: 'Documentation' }).getAttribute('href')).toBe('https://docs.example.org'); }); + it('should mark the source code link with the GitHub mark at the footer icon size', () => { + renderPanel(); + expect(sourceCodeMark().getAttribute('class')).toBe('h-4 w-4'); + }); + + it('should shrink the GitHub mark in preview mode, so it scales with the miniature footer', () => { + renderPanel(null, { preview: true }); + expect(sourceCodeMark().getAttribute('class')).toBe('h-3 w-3'); + }); + it('should hide the footer links when they are turned off', () => { renderPanel({ showFooterLinks: false }); expect(screen.queryByRole('link')).toBeNull(); diff --git a/packages/react-core/package.json b/packages/react-core/package.json index 7945830dd..037cfe73e 100644 --- a/packages/react-core/package.json +++ b/packages/react-core/package.json @@ -33,7 +33,7 @@ "@opendatacapture/subject-utils": "workspace:*", "http-status-codes": "^2.3.0", "lodash-es": "workspace:lodash-es__4.x@*", - "lucide-react": "^0.577.0", + "lucide-react": "^1.49.0", "motion": "catalog:", "react-dropzone": "catalog:", "serialize-error": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e690e5ab..bcaf8c2dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -590,8 +590,8 @@ importers: specifier: workspace:lodash-es__4.x@* version: link:../../vendor/lodash-es@4.x lucide-react: - specifier: ^0.577.0 - version: 0.577.0(react@vendor+react@19.x) + specifier: ^1.49.0 + version: 1.49.0(react@vendor+react@19.x) monaco-editor: specifier: ^0.54.0 version: 0.54.0 @@ -729,8 +729,8 @@ importers: specifier: workspace:lodash-es__4.x@* version: link:../../vendor/lodash-es@4.x lucide-react: - specifier: ^0.577.0 - version: 0.577.0(react@vendor+react@19.x) + specifier: ^1.49.0 + version: 1.49.0(react@vendor+react@19.x) motion: specifier: 'catalog:' version: 13.4.4(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) @@ -993,8 +993,8 @@ importers: specifier: workspace:lodash-es__4.x@* version: link:../../vendor/lodash-es@4.x lucide-react: - specifier: ^0.577.0 - version: 0.577.0(react@vendor+react@19.x) + specifier: ^1.49.0 + version: 1.49.0(react@vendor+react@19.x) motion: specifier: 'catalog:' version: 13.4.4(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) @@ -8356,15 +8356,14 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@0.577.0: - resolution: {integrity: sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - lucide-react@1.17.0: - resolution: {integrity: sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w==} + lucide-react@1.49.0: + resolution: {integrity: sha512-ApfaNM4/7jBrVopqLJ84Jz8TMQ1Q1JeZ1PmKh25aeS3E1FTVcMkSh1bEQB7dfzdIrwXRP8p3z4AJjTIHHb5IIA==} peerDependencies: + '@types/react': '*' react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} @@ -11865,7 +11864,7 @@ snapshots: class-variance-authority: 0.7.1 clsx: 2.1.1 lodash-es: 4.18.1 - lucide-react: 1.17.0(react@19.1.0) + lucide-react: 1.49.0(react@19.1.0) motion: 11.18.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) @@ -11922,7 +11921,7 @@ snapshots: class-variance-authority: 0.7.1 clsx: 2.1.1 lodash-es: 4.18.1 - lucide-react: 1.17.0(react@vendor+react@19.x) + lucide-react: 1.49.0(react@vendor+react@19.x) motion: 11.18.2(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) react: link:vendor/react@19.x react-dom: link:vendor/react-dom@19.x @@ -18419,15 +18418,11 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@0.577.0(react@vendor+react@19.x): - dependencies: - react: link:vendor/react@19.x - - lucide-react@1.17.0(react@19.1.0): + lucide-react@1.49.0(react@19.1.0): dependencies: react: 19.1.0 - lucide-react@1.17.0(react@vendor+react@19.x): + lucide-react@1.49.0(react@vendor+react@19.x): dependencies: react: link:vendor/react@19.x From 96bb76912889dd832900fe606befa545e581c4a1 Mon Sep 17 00:00:00 2001 From: joshunrau Date: Tue, 6 Oct 2026 22:49:02 -0400 Subject: [PATCH 07/14] build(playground): upgrade react-resizable-panels to 4 apps/playground moves react-resizable-panels from ^3.0.6 to ^4.14.1, the newest release past the 7-day release age (#1616). The lockfile drops 3.0.6; it still holds 2.1.9, which libui depends on for its own resizable component and which the playground does not use. Version 4 renames PanelGroup to Group, PanelResizeHandle to Separator and the direction prop to orientation, and reads a bare numeric size as pixels rather than a percentage. MainContent and the Resizable story now give every size as a percentage string ("66%", "25%" and so on), which keeps the two-to-one editor/preview split and the quarter minimum each pane had before. 4.x no longer renders data-panel-group-direction. The group sets its flex direction inline from its orientation, so the group wrapper drops its flex-col variant. The handle's stacked-layout styles now key on the aria-orientation the library puts on every separator, which is "horizontal" inside a vertical group, since the line between stacked panels is horizontal. The two panes get ids, which 4.x also renders as their data-testid. A new playground spec checks that the editor starts at about two thirds of the split, that dragging the separator moves it, and that it stops at a quarter; it fails when either editor size is written as a bare number. Unit tests render the wrappers with react-dom/server, since the playground project has no DOM, and pin the attributes their classes key on. The playground AGENTS.md and the testing strategy doc record both. Co-Authored-By: Claude Opus 5.5 --- .agents/docs/architecture/testing-strategy.md | 2 +- apps/playground/AGENTS.md | 20 ++++++--- apps/playground/package.json | 2 +- .../components/MainContent/MainContent.tsx | 7 ++-- .../Resizable/Resizable.stories.tsx | 12 +++--- .../components/Resizable/ResizableHandle.tsx | 10 +++-- .../Resizable/ResizablePanelGroup.tsx | 9 ++-- .../__tests__/ResizableHandle.test.tsx | 41 +++++++++++++++++++ .../__tests__/ResizablePanelGroup.test.tsx | 30 ++++++++++++++ pnpm-lock.yaml | 14 +++---- testing/src/pages/playground/index.page.ts | 23 +++++++++++ testing/src/specs/playground.spec.ts | 18 ++++++++ 12 files changed, 154 insertions(+), 34 deletions(-) create mode 100644 apps/playground/src/components/Resizable/__tests__/ResizableHandle.test.tsx create mode 100644 apps/playground/src/components/Resizable/__tests__/ResizablePanelGroup.test.tsx diff --git a/.agents/docs/architecture/testing-strategy.md b/.agents/docs/architecture/testing-strategy.md index b3f5cfe35..c8e86ad79 100644 --- a/.agents/docs/architecture/testing-strategy.md +++ b/.agents/docs/architecture/testing-strategy.md @@ -41,7 +41,7 @@ package's config, which is **not** always the directory name. | `instrument-interpreter` | `packages/instrument-interpreter` | | | `instrument-stubs` | `packages/instrument-stubs` | node environment; tests are `.test.js` (no tsconfig, so eslint cannot parse a `.ts` file here) | | `instrument-utils` | `packages/instrument-utils` | | -| `playground` | `apps/playground` | node environment; redeclares the `@` alias. Only `src/preview/protocol.ts` is unit-testable — the preview frame, its origin split and the message bridge are covered by `testing/src/specs/playground.spec.ts` | +| `playground` | `apps/playground` | node environment; redeclares the `@` alias. Pure code under `src/preview/`, plus components rendered to markup with `react-dom/server` (`src/components/Resizable/`) — the preview frame, its origin split, the message bridge and the editor/preview split are covered by `testing/src/specs/playground.spec.ts` | | `playground-url` | `packages/playground-url` | | | `react-core` | `packages/react-core` | `environment: 'happy-dom'` | | `release-info` | `packages/release-info` | | diff --git a/apps/playground/AGENTS.md b/apps/playground/AGENTS.md index 23f299117..a213815b1 100644 --- a/apps/playground/AGENTS.md +++ b/apps/playground/AGENTS.md @@ -150,6 +150,11 @@ directly means pasting a second share link into an open tab does nothing. Its `$ `.vscode/Scratch/` is unrelated junk. +The editor/preview split in `src/components/MainContent/MainContent.tsx` gives every +`react-resizable-panels` size as a percentage string. Version 4 reads a bare number as pixels, so +`defaultSize={66}` still type-checks and renders, at the wrong size; the split test in +`testing/src/specs/playground.spec.ts` measures the panes to catch it. + `__APP_VERSION__` and `__GITHUB_REPO_URL__` are the build-time defines, declared in `src/vite-env.d.ts` and supplied in `vite.config.ts`; `GITHUB_REPO_URL` must be listed under the `build` task's `env` in `turbo.json` or turbo's strict env mode hides it from the build. Anything @@ -157,12 +162,15 @@ that differs between deployments belongs in `/config.json` instead. ## Tests -`pnpm exec vitest --project playground`, from the repo root. The project runs in node and covers -only what is pure — `src/preview/__tests__/protocol.test.ts` pins the message schemas, error -serialization and `resolvePreviewOrigin`, and `config.test.ts` the `/config.json` contract. -Anything that touches esbuild-wasm, Monaco workers or `/runtime/v1` has no test environment here; -the frame itself, the origin split, the runtime config and the message bridge are exercised for real -by `testing/src/specs/playground.spec.ts`, which Playwright runs against this app's dev server. +`pnpm exec vitest --project playground`, from the repo root. The project runs in node, with no DOM, +and covers only what is pure — `src/preview/__tests__/protocol.test.ts` pins the message schemas, +error serialization and `resolvePreviewOrigin`, and `config.test.ts` the `/config.json` contract. A +component that renders on a server can still be pinned through `react-dom/server`'s +`renderToStaticMarkup`, as `src/components/Resizable/__tests__/` does for the attributes its classes +key on. Anything that touches esbuild-wasm, Monaco workers or `/runtime/v1` has no test environment +here; the frame itself, the origin split, the runtime config, the message bridge and the resizable +editor/preview split are exercised for real by `testing/src/specs/playground.spec.ts`, which +Playwright runs against this app's dev server. Storybook collects `*.stories.tsx` under `src/components/` centrally through `storybook/config/main.ts` under the `Playground Components` prefix. See diff --git a/apps/playground/package.json b/apps/playground/package.json index 2757f0309..cf57fb306 100644 --- a/apps/playground/package.json +++ b/apps/playground/package.json @@ -39,7 +39,7 @@ "react-dom": "workspace:react-dom__19.x@*", "react-dropzone": "catalog:", "react-error-boundary": "^6.1.6", - "react-resizable-panels": "^3.0.6", + "react-resizable-panels": "^4.14.1", "ts-pattern": "workspace:ts-pattern__5.x@*", "zod": "workspace:zod__3.x@*", "zustand": "catalog:" diff --git a/apps/playground/src/components/MainContent/MainContent.tsx b/apps/playground/src/components/MainContent/MainContent.tsx index 244962df9..25e88283c 100644 --- a/apps/playground/src/components/MainContent/MainContent.tsx +++ b/apps/playground/src/components/MainContent/MainContent.tsx @@ -11,12 +11,13 @@ export const MainContent = () => { return (
{isDesktop ? ( - - + // A Panel's id is also its data-testid, which is the only testid the library lets it carry. + + - + diff --git a/apps/playground/src/components/Resizable/Resizable.stories.tsx b/apps/playground/src/components/Resizable/Resizable.stories.tsx index e478f77a4..b3e7b227c 100644 --- a/apps/playground/src/components/Resizable/Resizable.stories.tsx +++ b/apps/playground/src/components/Resizable/Resizable.stories.tsx @@ -10,21 +10,21 @@ export const Default: Story = { args: { children: ( <> - +
One
- - - + + +
Two
- +
Three
@@ -34,6 +34,6 @@ export const Default: Story = { ), className: 'max-w-md rounded-lg border', - direction: 'horizontal' + orientation: 'horizontal' } }; diff --git a/apps/playground/src/components/Resizable/ResizableHandle.tsx b/apps/playground/src/components/Resizable/ResizableHandle.tsx index ce1847620..24a772cfc 100644 --- a/apps/playground/src/components/Resizable/ResizableHandle.tsx +++ b/apps/playground/src/components/Resizable/ResizableHandle.tsx @@ -1,14 +1,16 @@ import * as React from 'react'; import { cn } from '@douglasneuroinformatics/libui/utils'; -import { PanelResizeHandle } from 'react-resizable-panels'; +import { Separator } from 'react-resizable-panels'; -export type ResizableHandleProps = React.ComponentProps; +export type ResizableHandleProps = React.ComponentProps; +// The separator between stacked panels is a horizontal line, so the library marks the separator of a +// vertical group `aria-orientation="horizontal"`; the stacked-layout styles key on that. export const ResizableHandle = ({ className, ...props }: ResizableHandleProps) => ( - ; +export type ResizablePanelGroupProps = React.ComponentProps; export const ResizablePanelGroup = ({ className, ...props }: ResizablePanelGroupProps) => ( - + ); diff --git a/apps/playground/src/components/Resizable/__tests__/ResizableHandle.test.tsx b/apps/playground/src/components/Resizable/__tests__/ResizableHandle.test.tsx new file mode 100644 index 000000000..66d59a613 --- /dev/null +++ b/apps/playground/src/components/Resizable/__tests__/ResizableHandle.test.tsx @@ -0,0 +1,41 @@ +import { renderToStaticMarkup } from 'react-dom/server'; + +import { Panel } from 'react-resizable-panels'; +import type { Orientation } from 'react-resizable-panels'; +import { describe, expect, it } from 'vitest'; + +import { ResizableHandle } from '../ResizableHandle'; +import { ResizablePanelGroup } from '../ResizablePanelGroup'; + +const renderSeparator = (orientation: Orientation) => { + const markup = renderToStaticMarkup( + + + + + + ); + const tag = /]*role="separator"[^>]*>/.exec(markup)![0]; + const attribute = (name: string) => new RegExp(`${name}="([^"]*)"`).exec(tag)![1]!; + return { ariaOrientation: attribute('aria-orientation'), className: attribute('class') }; +}; + +const styledOrientations = (className: string) => { + return new Set(Array.from(className.matchAll(/aria-\[orientation=(\w+)\]:/g), ([, orientation]) => orientation)); +}; + +describe('ResizableHandle', () => { + it('should apply its stacked-layout styles in a vertical group, keyed on the aria-orientation the library renders there', () => { + const { ariaOrientation, className } = renderSeparator('vertical'); + expect(styledOrientations(className)).toStrictEqual(new Set([ariaOrientation])); + }); + + it('should leave its stacked-layout styles off in a horizontal group, so the handle stays a vertical bar', () => { + const { ariaOrientation, className } = renderSeparator('horizontal'); + expect(styledOrientations(className)).not.toContain(ariaOrientation); + }); + + it('should keep the caller class name, which MainContent uses to set the margin beside the preview', () => { + expect(renderSeparator('horizontal').className.split(' ')).toContain('mr-12'); + }); +}); diff --git a/apps/playground/src/components/Resizable/__tests__/ResizablePanelGroup.test.tsx b/apps/playground/src/components/Resizable/__tests__/ResizablePanelGroup.test.tsx new file mode 100644 index 000000000..57052d0aa --- /dev/null +++ b/apps/playground/src/components/Resizable/__tests__/ResizablePanelGroup.test.tsx @@ -0,0 +1,30 @@ +import { renderToStaticMarkup } from 'react-dom/server'; + +import { Panel } from 'react-resizable-panels'; +import type { Orientation } from 'react-resizable-panels'; +import { describe, expect, it } from 'vitest'; + +import { ResizablePanelGroup } from '../ResizablePanelGroup'; + +const renderGroup = (orientation: Orientation) => { + const markup = renderToStaticMarkup( + + + + ); + return /]*data-group[^>]*>/.exec(markup)![0]; +}; + +describe('ResizablePanelGroup', () => { + it('should stack the panels of a vertical group in a column, which no class of its own does any more', () => { + expect(renderGroup('vertical')).toContain('flex-direction:column'); + }); + + it('should lay the panels of a horizontal group out in a row', () => { + expect(renderGroup('horizontal')).toContain('flex-direction:row'); + }); + + it('should keep the caller class name alongside its own', () => { + expect(renderGroup('horizontal')).toContain('class="flex h-full w-full rounded-lg"'); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bcaf8c2dc..3a8b78642 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -614,8 +614,8 @@ importers: specifier: ^6.1.6 version: 6.1.6(react@vendor+react@19.x) react-resizable-panels: - specifier: ^3.0.6 - version: 3.0.6(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) + specifier: ^4.14.1 + version: 4.14.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) ts-pattern: specifier: workspace:ts-pattern__5.x@* version: link:../../vendor/ts-pattern@5.x @@ -9438,11 +9438,11 @@ packages: react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - react-resizable-panels@3.0.6: - resolution: {integrity: sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==} + react-resizable-panels@4.14.1: + resolution: {integrity: sha512-OB1bXDNTLcGgTTbaX6Dn5efZhlMboSnkfr1w4xpsJTHPEbRJpACBhXpgRCXozgPO1ztgFeZRUe5J3mFNCVKm5g==} peerDependencies: - react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 react-smooth@4.0.4: resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==} @@ -19825,7 +19825,7 @@ snapshots: react: link:vendor/react@19.x react-dom: link:vendor/react-dom@19.x - react-resizable-panels@3.0.6(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x): + react-resizable-panels@4.14.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x): dependencies: react: link:vendor/react@19.x react-dom: link:vendor/react-dom@19.x diff --git a/testing/src/pages/playground/index.page.ts b/testing/src/pages/playground/index.page.ts index 2a3f01d16..012ea8ad5 100644 --- a/testing/src/pages/playground/index.page.ts +++ b/testing/src/pages/playground/index.page.ts @@ -8,14 +8,37 @@ import { playgroundURL } from '../../support/env'; */ export class PlaygroundPage { readonly $ref: Page; + readonly editorPane: Locator; + /** The handle between the editor and preview panes, which resizes them. */ + readonly paneSeparator: Locator; /** The instrument preview, which runs on another origin than the editor. */ readonly preview: FrameLocator; readonly previewFrame: Locator; + readonly previewPane: Locator; constructor(page: Page) { this.$ref = page; + this.editorPane = page.getByTestId('editor-pane'); + this.paneSeparator = page.getByRole('separator'); this.previewFrame = page.getByTestId('preview-frame'); this.preview = page.frameLocator('[data-testid="preview-frame"]'); + this.previewPane = page.getByTestId('preview-pane'); + } + + /** Drags the pane separator to `x`, in page coordinates. */ + async dragPaneSeparatorTo(x: number): Promise { + const separator = (await this.paneSeparator.boundingBox())!; + const y = separator.y + separator.height / 2; + await this.$ref.mouse.move(separator.x + separator.width / 2, y); + await this.$ref.mouse.down(); + await this.$ref.mouse.move(x, y, { steps: 10 }); + await this.$ref.mouse.up(); + } + + /** The editor's share of the width the editor and preview panes divide between them, from 0 to 1. */ + async editorShare(): Promise { + const [editor, preview] = await Promise.all([this.editorPane.boundingBox(), this.previewPane.boundingBox()]); + return editor!.width / (editor!.width + preview!.width); } /** Opens the editor, or a share link built against {@link playgroundURL}. */ diff --git a/testing/src/specs/playground.spec.ts b/testing/src/specs/playground.spec.ts index 0a143a0d3..f5d63c5cd 100644 --- a/testing/src/specs/playground.spec.ts +++ b/testing/src/specs/playground.spec.ts @@ -245,4 +245,22 @@ test.describe('playground', () => { await expect(playground.preview.getByText('Pet Name Measure')).toBeVisible(); }); + + // react-resizable-panels reads a bare number as pixels, so a pane size written as `66` rather than + // `"66%"` still type-checks and renders, only at the wrong size. + test('should split the editor and preview two to one, and stop the editor at a quarter when dragged narrower', async ({ + page + }) => { + const playground = new PlaygroundPage(page); + await playground.goto(); + await expect(playground.editorPane).toBeVisible({ timeout: PREVIEW_TIMEOUT }); + await expect.poll(() => playground.editorShare()).toBeCloseTo(0.66, 1); + + const editor = (await playground.editorPane.boundingBox())!; + await playground.dragPaneSeparatorTo(editor.x + editor.width / 2); + await expect.poll(() => playground.editorShare()).toBeCloseTo(0.33, 1); + + await playground.dragPaneSeparatorTo(editor.x); + await expect.poll(() => playground.editorShare()).toBeCloseTo(0.25, 1); + }); }); From fb4f73fa6588d0255c6a3922848f0e2f89bfb7ea Mon Sep 17 00:00:00 2001 From: joshunrau Date: Wed, 7 Oct 2026 08:40:00 -0400 Subject: [PATCH 08/14] build(instrument-bundler): upgrade es-module-lexer to 3 instrument-bundler moves es-module-lexer from ^2.3.2 to ^3.0.2, the newest release past the 7-day release age (#1617). 3.0.3 is two days old. parse.ts now reads the primary API's records rather than importing es-module-lexer/minimal. The minimal entry point is documented and supported, not a shim on its way out: the README presents it as the v2-shaped build for size-sensitive embedders, and es-module-shims uses it. But the full build gives parse.ts everything it reads, the specifier and statement ranges, and its tagged union replaces the d === -2 and d > -1 sentinels that parseImports used to tell import.meta and dynamic imports apart. The full build needs Wasm SIMD (Chrome 91, Firefox 89, Safari 16.4) and also lexes TypeScript type-only syntax. Neither matters here: the lexer reaches a browser only in the playground, since the web and gateway production bundles tree-shake it out, and it only ever reads esbuild's JavaScript output. 3.0 drops the runtime ImportType enum, so ImportType[t] threw. An explicit map from the lexer's own Import['type'] union to the Static, Dynamic and ImportMeta kinds replaces it, typed as a mapped type so a variant added to the union is a compile error. 3.0 also reports the module request of export * as its own reexport-star kind and lists the star re-export among the exports. The map classifies the request as static, as 2.x did, and parse leaves the star re-export out of its export names, since it exports no name of its own. Neither can reach transformImports, because the build asserts the output has no exports. The bundles built from the instrument library, the playground examples and the bundler's fixtures are byte-identical to those 2.3.2 produced. In 3.x init is a function, so `await init;` awaited nothing. parse.ts now calls `await init()`. Without it the lexer compiles its Wasm synchronously inside the first parse, which Node, Chromium and Firefox accept for a module this size, so nothing failed visibly. The library still requires init before parse on a browser main thread, and the playground's production bundle keeps the awaited call. parse.test.ts covers each kind the map handles, the star re-export case, and that init resolves before the first parse. The instrument-bundler AGENTS.md records where a lexer upgrade is ported and why the init call stays. The lockfile keeps 2.3.2 for vitest and libnest and 1.7.0 for astro, whose ranges exclude 3. Co-Authored-By: Claude Opus 5.5 --- packages/instrument-bundler/AGENTS.md | 12 ++- packages/instrument-bundler/package.json | 2 +- .../src/__tests__/parse.test.ts | 81 ++++++++++++++----- packages/instrument-bundler/src/parse.ts | 38 +++++---- pnpm-lock.yaml | 9 ++- 5 files changed, 101 insertions(+), 41 deletions(-) diff --git a/packages/instrument-bundler/AGENTS.md b/packages/instrument-bundler/AGENTS.md index ad61fdf64..3aaceccfd 100644 --- a/packages/instrument-bundler/AGENTS.md +++ b/packages/instrument-bundler/AGENTS.md @@ -68,7 +68,17 @@ bundle dropped the entire module and every instrument import failed with `pnpm dev`. `src/vendor/__tests__/esbuild.test.ts` bundles the module with tree shaking to guard this. **`src/parse.ts` is vendored** from `parse-imports` (Apache-2.0, adapted to TypeScript and to run in a -browser). Treat it as third-party: fix it upstream-style or not at all. +browser). Treat it as third-party: fix it upstream-style or not at all. The exception is `parse`, the +adapter over `es-module-lexer`'s records, which upstream does not have: an `es-module-lexer` upgrade is +ported there. Its `importTypes` map must classify every import `type` the lexer reports, so a new one +is a compile error rather than an `undefined`. + +**Nothing visibly fails without the top-level `await init()` in `src/parse.ts`.** `es-module-lexer` +falls back to compiling its Wasm synchronously inside the first `parse`, which Node, Chromium and +Firefox all allow for a module this size, so the CLI, the unit tests and the playground keep working +if the line goes. It stays because the library requires `init()` before `parse` on a browser main +thread, where its README says synchronous compilation is restricted and where it would block the page. +`src/__tests__/parse.test.ts` checks that `init` resolves before the first `parse`. ## Module resolution diff --git a/packages/instrument-bundler/package.json b/packages/instrument-bundler/package.json index de6096d3f..f3ee860e1 100644 --- a/packages/instrument-bundler/package.json +++ b/packages/instrument-bundler/package.json @@ -35,7 +35,7 @@ "dependencies": { "@opendatacapture/runtime-internal": "workspace:*", "commander": "catalog:", - "es-module-lexer": "^2.3.2", + "es-module-lexer": "^3.0.2", "esbuild": "catalog:", "esbuild-wasm": "catalog:", "glob": "^13.0.6", diff --git a/packages/instrument-bundler/src/__tests__/parse.test.ts b/packages/instrument-bundler/src/__tests__/parse.test.ts index 81aeda60f..8a40d2e2d 100644 --- a/packages/instrument-bundler/src/__tests__/parse.test.ts +++ b/packages/instrument-bundler/src/__tests__/parse.test.ts @@ -1,8 +1,25 @@ -import { describe, expect, it, test } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { InstrumentBundlerError } from '../error.js'; import { parse, parseImports, parseModuleSpecifier } from '../parse.js'; +const lexerCalls = vi.hoisted(() => [] as ('init' | 'parse')[]); + +vi.mock('es-module-lexer', async (importOriginal) => { + const lexer = await importOriginal(); + return { + ...lexer, + init: async () => { + await lexer.init(); + lexerCalls.push('init'); + }, + parse: (source: string) => { + lexerCalls.push('parse'); + return lexer.parse(source); + } + }; +}); + const code = ` import React, { useState } from 'react'; import { App } from './App.tsx'; @@ -14,27 +31,47 @@ export const foo = null; export default foo; `; -test('parse', () => { - const result = parse(code); - expect(result).toMatchObject({ - exports: [{ exportName: 'foo' }, { exportName: 'default' }], - imports: [ - { - importPath: 'react', - importType: 'Static', - statement: "import React, { useState } from 'react'" - }, - { - importPath: './App.tsx', - importType: 'Static', - statement: "import { App } from './App.tsx'" - }, - { - importPath: '/hello.js', - importType: 'Dynamic', - statement: "import('/hello.js')" - } - ] +describe('parse', () => { + it('should finish compiling the lexer before it first parses, so parse never compiles it synchronously on a browser main thread', () => { + parse(code); + expect(lexerCalls.slice(0, 2)).toEqual(['init', 'parse']); + }); + + it('should report the path, type and statement of each import and the name of each export', () => { + expect(parse(code)).toMatchObject({ + exports: [{ exportName: 'foo' }, { exportName: 'default' }], + imports: [ + { + importPath: 'react', + importType: 'Static', + statement: "import React, { useState } from 'react'" + }, + { + importPath: './App.tsx', + importType: 'Static', + statement: "import { App } from './App.tsx'" + }, + { + importPath: '/hello.js', + importType: 'Dynamic', + statement: "import('/hello.js')" + } + ] + }); + }); + + it('should classify import.meta as ImportMeta, so parseImports can tell it apart from an import', () => { + expect(parse(`const url = import.meta.url;`).imports).toMatchObject([{ importType: 'ImportMeta' }]); + }); + + it('should classify the module request of a star re-export as a static import', () => { + expect(parse(`export * from './core.js';`).imports).toMatchObject([ + { importPath: './core.js', importType: 'Static', statement: `export * from './core.js'` } + ]); + }); + + it('should leave a star re-export out of the exports, since it exports no name of its own', () => { + expect(parse(`export * from './core.js';`).exports).toEqual([]); }); }); diff --git a/packages/instrument-bundler/src/parse.ts b/packages/instrument-bundler/src/parse.ts index 36a7e94dd..75179b15f 100644 --- a/packages/instrument-bundler/src/parse.ts +++ b/packages/instrument-bundler/src/parse.ts @@ -19,12 +19,13 @@ * limitations under the License. */ -import { parse as _parse, ImportType, init } from 'es-module-lexer'; +import { parse as _parse, init } from 'es-module-lexer'; +import type { Import as LexedImport } from 'es-module-lexer'; import { removeSlashes } from 'slashes'; import { InstrumentBundlerError } from './error.js'; -await init; +await init(); /** * A type representing what kind of module a specifier refers to. @@ -284,19 +285,26 @@ export function parseModuleSpecifier(moduleSpecifierString: string, { isDynamicI } as const; } +// es-module-lexer 2 reported the module request of `export * from` as a static import +const importTypes = { + dynamic: 'Dynamic', + 'import-meta': 'ImportMeta', + 'reexport-star': 'Static', + static: 'Static' +} as const satisfies { [TLexedType in LexedImport['type']]: 'Dynamic' | 'ImportMeta' | 'Static' }; + export function parse(code: string) { const [imports, exports] = _parse(code); return { - exports: exports.map(({ n }) => ({ exportName: n })), - imports: imports.map(({ d, e, n, s, se, ss, t }) => ({ - dynamicImportStartIndex: d, - importPath: n, - importType: ImportType[t], - moduleSpecifierEndIndexExclusive: e, - moduleSpecifierStartIndex: s, - statement: code.slice(ss, se), - statementEndIndex: se, - statementStartIndex: ss + exports: exports.filter((record) => record.type !== 'reexport-all').map(({ name }) => ({ exportName: name })), + imports: imports.map(({ end, importEnd, importStart, specifier, start, type }) => ({ + importPath: specifier, + importType: importTypes[type], + moduleSpecifierEndIndexExclusive: end, + moduleSpecifierStartIndex: start, + statement: code.slice(importStart, importEnd), + statementEndIndex: importEnd, + statementStartIndex: importStart })) }; } @@ -308,18 +316,18 @@ export function parseImports(code: string): Iterable { *[Symbol.iterator]() { for (let { // eslint-disable-next-line prefer-const - dynamicImportStartIndex, + importType, moduleSpecifierEndIndexExclusive, moduleSpecifierStartIndex, // eslint-disable-next-line prefer-const statementStartIndex } of imports) { - const isImportMeta = dynamicImportStartIndex === -2; + const isImportMeta = importType === 'ImportMeta'; if (isImportMeta) { continue; } - const isDynamicImport = dynamicImportStartIndex > -1; + const isDynamicImport = importType === 'Dynamic'; // Include string literal quotes in character range if (!isDynamicImport) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a8b78642..b3a440b4e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -826,8 +826,8 @@ importers: specifier: 'catalog:' version: 15.0.0 es-module-lexer: - specifier: ^2.3.2 - version: 2.3.2 + specifier: ^3.0.2 + version: 3.0.2 esbuild: specifier: 'catalog:' version: 0.28.2 @@ -7074,6 +7074,9 @@ packages: es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + es-module-lexer@3.0.2: + resolution: {integrity: sha512-BuIB67FngDSyQ/dpQNOZybwdEBDUGJQvOqwWr4ha/ufYiqzuEwPkKO2zLhRAgay28tStRIHUeWmszZAJo3GCOg==} + es-object-atoms@1.1.2: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} @@ -16807,6 +16810,8 @@ snapshots: es-module-lexer@2.3.2: {} + es-module-lexer@3.0.2: {} + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 From 10625570232b67b959b053966de36da9999aa3c3 Mon Sep 17 00:00:00 2001 From: joshunrau Date: Wed, 7 Oct 2026 09:45:30 -0400 Subject: [PATCH 09/14] build(web): upgrade recharts to 3 recharts 3 makes react-is a required peer dependency rather than installing its own copy, so apps/web now declares react-is explicitly. It comes from the react19 catalog, pinned to 19.1.0 beside react and react-dom, so it matches the React release the app runs through vendor/react@19.x. recharts 3 sorts legend and tooltip items alphabetically by default, so LineGraph passes both an itemSorter that keeps them in the order its lines are given, as they were under 2.x. The tooltip labelFormatter now receives its label typed as a ReactNode, so the timestamp is converted with Number() instead of being annotated as a number. The LineGraph unit test follows recharts 3 into its new DOM. Axis tick labels now render in a separate z-index layer, and a mouse move is read from clientX and clientY and handled on the next animation frame. Co-Authored-By: Claude Opus 5.5 --- apps/web/package.json | 3 +- .../src/components/LineGraph/LineGraph.tsx | 12 +- .../LineGraph/__tests__/LineGraph.test.tsx | 26 ++- pnpm-lock.yaml | 180 ++++++++++-------- pnpm-workspace.yaml | 1 + 5 files changed, 128 insertions(+), 94 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 2790dc486..b93e66093 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -48,7 +48,8 @@ "qrcode": "^1.5.4", "react": "workspace:react__19.x@*", "react-dom": "workspace:react-dom__19.x@*", - "recharts": "^2.15.2", + "react-is": "catalog:react19", + "recharts": "^3.10.1", "ts-pattern": "workspace:ts-pattern__5.x@*", "type-fest": "workspace:type-fest__4.x@*", "xlsx": "^0.18.5", diff --git a/apps/web/src/components/LineGraph/LineGraph.tsx b/apps/web/src/components/LineGraph/LineGraph.tsx index 0fad927a9..56576105f 100644 --- a/apps/web/src/components/LineGraph/LineGraph.tsx +++ b/apps/web/src/components/LineGraph/LineGraph.tsx @@ -15,7 +15,7 @@ import { XAxis, YAxis } from 'recharts'; -import type { LineProps } from 'recharts'; +import type { LegendPayload, LineProps } from 'recharts'; import type { ConditionalKeys } from 'type-fest'; /** An array of arbitrary objects with data to graph */ @@ -69,6 +69,8 @@ function LineGraphComponent({ const { resolvedLanguage } = useTranslation('libui'); const [theme] = useTheme(); + const lineIndex = ({ dataKey }: Pick) => lines.findIndex(({ val }) => val === dataKey); + return ( @@ -99,8 +101,10 @@ function LineGraphComponent({ /> { - const date = new Date(time); + itemSorter={lineIndex} + // recharts types every tooltip label as a ReactNode, but on this number axis it is the x value + labelFormatter={(time) => { + const date = new Date(Number(time)); return new Intl.DateTimeFormat(resolvedLanguage, { dateStyle: 'full', timeStyle: 'medium' @@ -120,7 +124,7 @@ function LineGraphComponent({ {err && } ))} - + ); diff --git a/apps/web/src/components/LineGraph/__tests__/LineGraph.test.tsx b/apps/web/src/components/LineGraph/__tests__/LineGraph.test.tsx index 63ee7b704..c8cba20cf 100644 --- a/apps/web/src/components/LineGraph/__tests__/LineGraph.test.tsx +++ b/apps/web/src/components/LineGraph/__tests__/LineGraph.test.tsx @@ -39,15 +39,14 @@ const renderGraph = ( const lineCurves = (container: HTMLElement) => [...container.querySelectorAll('.recharts-line-curve')]; const xAxisTicks = (container: HTMLElement) => - [...container.querySelectorAll('.recharts-xAxis .recharts-cartesian-axis-tick-value')].map( + [...container.querySelectorAll('.recharts-xAxis-tick-labels .recharts-cartesian-axis-tick-value')].map( (tick) => tick.textContent ); -/** happy-dom's MouseEvent has no pageX/pageY, which recharts reads to find the hovered point. */ -const hover = (container: HTMLElement, { pageX, pageY }: { pageX: number; pageY: number }) => { - const event = new MouseEvent('mousemove', { bubbles: true }); - Object.defineProperties(event, { pageX: { value: pageX }, pageY: { value: pageY } }); - fireEvent(container.querySelector('.recharts-wrapper')!, event); +/** Recharts handles a mouse move on the next animation frame, so wait for one before reading the tooltip. */ +const hover = async (container: HTMLElement, { clientX, clientY }: { clientX: number; clientY: number }) => { + fireEvent.mouseMove(container.querySelector('.recharts-wrapper')!, { clientX, clientY }); + await act(() => new Promise((resolve) => requestAnimationFrame(() => resolve()))); }; describe('LineGraph', () => { @@ -74,14 +73,23 @@ describe('LineGraph', () => { expect(lineCurves(renderGraph([scoreLine], undefined))).toHaveLength(1); }); - it('should label the tooltip with the full date in the interface language', () => { + it('should label the tooltip with the full date in the interface language', async () => { void i18n.changeLanguage('fr'); const container = renderGraph(); - hover(container, { pageX: 420, pageY: 200 }); + await hover(container, { clientX: 420, clientY: 200 }); expect(container.querySelector('.recharts-tooltip-label')?.textContent).toMatch(/^vendredi 2 janvier 2026/); }); - it('should name each line in the legend', () => { + it('should list the hovered values in the tooltip in the order the lines are given, not alphabetically', async () => { + const container = renderGraph([scoreLine, { name: 'Error', val: 'scoreError' }]); + await hover(container, { clientX: 420, clientY: 200 }); + expect([...container.querySelectorAll('.recharts-tooltip-item-name')].map((item) => item.textContent)).toEqual([ + 'Score', + 'Error' + ]); + }); + + it('should name each line in the legend in the order the lines are given, not alphabetically', () => { const container = renderGraph([scoreLine, { name: 'Error', val: 'scoreError' }]); expect([...container.querySelectorAll('.recharts-legend-item-text')].map((item) => item.textContent)).toEqual([ 'Score', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b3a440b4e..b8e2bc075 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,6 +124,9 @@ catalogs: react-dom: specifier: 19.1.0 version: 19.1.0 + react-is: + specifier: 19.1.0 + version: 19.1.0 overrides: '@types/react': '-' @@ -749,9 +752,12 @@ importers: react-dom: specifier: workspace:react-dom__19.x@* version: link:../../vendor/react-dom@19.x + react-is: + specifier: catalog:react19 + version: 19.1.0 recharts: - specifier: ^2.15.2 - version: 2.15.4(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) + specifier: ^3.10.1 + version: 3.10.1(react-dom@vendor+react-dom@19.x)(react-is@19.1.0)(react@vendor+react@19.x)(redux@5.0.1) ts-pattern: specifier: workspace:ts-pattern__5.x@* version: link:../../vendor/ts-pattern@5.x @@ -4908,6 +4914,17 @@ packages: '@radix-ui/rect@1.1.3': resolution: {integrity: sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==} + '@reduxjs/toolkit@2.13.0': + resolution: {integrity: sha512-f6jwVRfwW30+tnllz4YcsHHpPYYOE7abcHkyUCovD+P7DyhdAI5prz2Fh/8TLhakjpN+7aIEion1s5L/tvlYlA==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} @@ -5221,6 +5238,9 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@storybook/addon-docs@10.5.6': resolution: {integrity: sha512-zyUJBrrpC9NTrmsREaVFNr+9WW6pikJtmRvo7GgZGqthEyhjQKSarHrW0aNWkwae2ep3jp1CZi8vIUVG1Dnp0w==} peerDependencies: @@ -5876,6 +5896,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -6950,9 +6973,6 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} - dom-helpers@5.2.1: - resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -7328,9 +7348,6 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} @@ -7368,10 +7385,6 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-equals@5.4.0: - resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==} - engines: {node: '>=6.0.0'} - fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} @@ -8339,9 +8352,6 @@ packages: lodash.once@4.1.1: resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - lodash@4.18.1: - resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -9412,8 +9422,20 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-is@19.1.0: + resolution: {integrity: sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==} + + react-redux@9.3.0: + resolution: {integrity: sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==} + peerDependencies: + '@types/react': '*' + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} @@ -9447,12 +9469,6 @@ packages: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - react-smooth@4.0.4: - resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} @@ -9463,12 +9479,6 @@ packages: '@types/react': optional: true - react-transition-group@4.4.5: - resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} - peerDependencies: - react: '>=16.6.0' - react-dom: '>=16.6.0' - react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -9502,16 +9512,13 @@ packages: resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} engines: {node: '>= 4'} - recharts-scale@0.4.5: - resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} - - recharts@2.15.4: - resolution: {integrity: sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==} - engines: {node: '>=14'} - deprecated: 1.x and 2.x branches are no longer active. Bump to Recharts v3 to receive latest features and bugfixes. See https://github.com/recharts/recharts/wiki/3.0-migration-guide + recharts@3.10.1: + resolution: {integrity: sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==} + engines: {node: '>=18'} peerDependencies: - react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 recma-build-jsx@1.0.0: resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} @@ -9531,6 +9538,14 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} @@ -10590,8 +10605,8 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - victory-vendor@36.9.2: - resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + victory-vendor@37.3.6: + resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} vite-plugin-compression@0.5.1: resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==} @@ -14414,6 +14429,18 @@ snapshots: '@radix-ui/rect@1.1.3': {} + '@reduxjs/toolkit@2.13.0(react-redux@9.3.0(react@vendor+react@19.x)(redux@5.0.1))(react@vendor+react@19.x)': + dependencies: + '@standard-schema/spec': 1.1.0 + '@standard-schema/utils': 0.3.0 + immer: 11.1.18 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.2.0 + optionalDependencies: + react: link:vendor/react@19.x + react-redux: 9.3.0(react@vendor+react@19.x)(redux@5.0.1) + '@rolldown/pluginutils@1.0.1': {} '@rollup/pluginutils@5.4.0(rollup@4.61.1)': @@ -14692,6 +14719,8 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@standard-schema/utils@0.3.0': {} + '@storybook/addon-docs@10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: '@mdx-js/react': 3.1.1(react@18.3.1) @@ -15453,6 +15482,8 @@ snapshots: '@types/unist@3.0.3': {} + '@types/use-sync-external-store@0.0.6': {} + '@types/webidl-conversions@7.0.3': {} '@types/whatwg-mimetype@3.0.2': {} @@ -16632,11 +16663,6 @@ snapshots: dom-accessibility-api@0.6.3: {} - dom-helpers@5.2.1: - dependencies: - '@babel/runtime': 7.29.7 - csstype: 3.2.3 - dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -17241,8 +17267,6 @@ snapshots: etag@1.8.1: {} - eventemitter3@4.0.7: {} - eventemitter3@5.0.4: {} events-universal@1.0.1: @@ -17307,8 +17331,6 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-equals@5.4.0: {} - fast-fifo@1.3.2: {} fast-glob@3.3.3: @@ -18407,8 +18429,6 @@ snapshots: lodash.once@4.1.1: {} - lodash@4.18.1: {} - longest-streak@3.1.0: {} loose-envify@1.4.0: @@ -19788,7 +19808,15 @@ snapshots: react-is@17.0.2: {} - react-is@18.3.1: {} + react-is@19.1.0: {} + + react-redux@9.3.0(react@vendor+react@19.x)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: link:vendor/react@19.x + use-sync-external-store: 1.6.0(react@vendor+react@19.x) + optionalDependencies: + redux: 5.0.1 react-remove-scroll-bar@2.3.8(react@19.1.0): dependencies: @@ -19835,14 +19863,6 @@ snapshots: react: link:vendor/react@19.x react-dom: link:vendor/react-dom@19.x - react-smooth@4.0.4(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x): - dependencies: - fast-equals: 5.4.0 - prop-types: 15.8.1 - react: link:vendor/react@19.x - react-dom: link:vendor/react-dom@19.x - react-transition-group: 4.4.5(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) - react-style-singleton@2.2.3(react@19.1.0): dependencies: get-nonce: 1.0.1 @@ -19855,15 +19875,6 @@ snapshots: react: link:vendor/react@19.x tslib: 2.8.1 - react-transition-group@4.4.5(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x): - dependencies: - '@babel/runtime': 7.29.7 - dom-helpers: 5.2.1 - loose-envify: 1.4.0 - prop-types: 15.8.1 - react: link:vendor/react@19.x - react-dom: link:vendor/react-dom@19.x - react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -19898,22 +19909,25 @@ snapshots: tiny-invariant: 1.3.3 tslib: 2.8.1 - recharts-scale@0.4.5: - dependencies: - decimal.js-light: 2.5.1 - - recharts@2.15.4(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x): + recharts@3.10.1(react-dom@vendor+react-dom@19.x)(react-is@19.1.0)(react@vendor+react@19.x)(redux@5.0.1): dependencies: + '@reduxjs/toolkit': 2.13.0(react-redux@9.3.0(react@vendor+react@19.x)(redux@5.0.1))(react@vendor+react@19.x) clsx: 2.1.1 - eventemitter3: 4.0.7 - lodash: 4.18.1 + decimal.js-light: 2.5.1 + es-toolkit: 1.52.0 + eventemitter3: 5.0.4 + immer: 11.1.18 react: link:vendor/react@19.x react-dom: link:vendor/react-dom@19.x - react-is: 18.3.1 - react-smooth: 4.0.4(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) - recharts-scale: 0.4.5 + react-is: 19.1.0 + react-redux: 9.3.0(react@vendor+react@19.x)(redux@5.0.1) + reselect: 5.2.0 tiny-invariant: 1.3.3 - victory-vendor: 36.9.2 + use-sync-external-store: 1.6.0(react@vendor+react@19.x) + victory-vendor: 37.3.6 + transitivePeerDependencies: + - '@types/react' + - redux recma-build-jsx@1.0.0: dependencies: @@ -19949,6 +19963,12 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + reflect-metadata@0.2.2: {} reflect.getprototypeof@1.0.10: @@ -21210,7 +21230,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - victory-vendor@36.9.2: + victory-vendor@37.3.6: dependencies: '@types/d3-array': 3.2.2 '@types/d3-ease': 3.0.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index bab606153..1f97543df 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -54,6 +54,7 @@ catalogs: react19: react: 19.1.0 react-dom: 19.1.0 + react-is: 19.1.0 minimumReleaseAge: 10080 # 7 days minimumReleaseAgeExclude: - '@douglasneuroinformatics/*' From f98c86586161c12971cf08e5231ef707217187df Mon Sep 17 00:00:00 2001 From: joshunrau Date: Wed, 7 Oct 2026 10:03:27 -0400 Subject: [PATCH 10/14] build: upgrade storybook to 10.6 The catalog moves storybook, @storybook/addon-docs, @storybook/addon-themes and @storybook/react-vite from an exact 10.5.6 pin back to ^10.6.1, the newest release past the 7-day release age (#1619). All four resolve to 10.6.1. From 10.5.7 the storybook package's published declarations import @storybook/react (import("@storybook/react").Args and .Parameters, from the declarations of StoryStore, DocsContext and the open-service types), although storybook does not depend on it. Under pnpm's default hoisting that import resolves to node_modules/.pnpm/node_modules, where @storybook/react is the peer variant installed for the storybook workspace's real react. The stories reach a second variant, paired with vendor/react@19.x, through @storybook/react-vite. TypeScript treats two copies with the same name and version as one package and keeps the first it loads, so the stories were checked against the hoisted copy. That copy resolves react to react@19.1.0, which has no types here because root package.json deletes @types/react, so ComponentProps was unresolved and StoryObj gave args no type. In apps/playground, apps/web and packages/react-core every story's args were unchecked, and the seven callbacks in args failed TS7006. pnpm-workspace.yaml now sets hoistPattern to everything except @storybook/react. The import in storybook's declarations no longer resolves, the stories load only their own copy, and their args are typed from the component's props again. A misspelled arg or a callback of the wrong signature is a type error, inside a decorator's too, and no story needed changing. The storybook AGENTS.md records why the exclusion must stay. An existing checkout keeps the stale hoisted link after a plain pnpm install, which records the new pattern and reports it is already up to date. Delete the root node_modules and install again; afterwards node_modules/.pnpm/node_modules/@storybook/react must not exist. Co-Authored-By: Claude Opus 5.5 --- pnpm-lock.yaml | 474 +++++++++++++++++++------------------------- pnpm-workspace.yaml | 14 +- storybook/AGENTS.md | 18 ++ 3 files changed, 229 insertions(+), 277 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b8e2bc075..eb093daac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,14 +43,14 @@ catalogs: specifier: ^6.9.0 version: 6.19.3 '@storybook/addon-docs': - specifier: 10.5.6 - version: 10.5.6 + specifier: ^10.6.1 + version: 10.6.1 '@storybook/addon-themes': - specifier: 10.5.6 - version: 10.5.6 + specifier: ^10.6.1 + version: 10.6.1 '@storybook/react-vite': - specifier: 10.5.6 - version: 10.5.6 + specifier: ^10.6.1 + version: 10.6.1 '@tailwindcss/postcss': specifier: 4.3.3 version: 4.3.3 @@ -103,8 +103,8 @@ catalogs: specifier: ^13.0.1 version: 13.0.1 storybook: - specifier: 10.5.6 - version: 10.5.6 + specifier: ^10.6.1 + version: 10.6.1 tailwindcss: specifier: 4.3.3 version: 4.3.3 @@ -634,7 +634,7 @@ importers: version: link:../../packages/vite-plugin-runtime '@storybook/react-vite': specifier: 'catalog:' - version: 10.5.6(esbuild@0.28.2)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) '@tailwindcss/vite': specifier: 'catalog:' version: 4.3.3(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) @@ -782,7 +782,7 @@ importers: version: link:../../packages/vite-plugin-runtime '@storybook/react-vite': specifier: 'catalog:' - version: 10.5.6(esbuild@0.28.2)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) '@tailwindcss/vite': specifier: 'catalog:' version: 4.3.3(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) @@ -1037,7 +1037,7 @@ importers: version: link:../instrument-stubs '@storybook/react-vite': specifier: 'catalog:' - version: 10.5.6(esbuild@0.28.2)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) '@testing-library/dom': specifier: 'catalog:' version: 10.4.2 @@ -1383,13 +1383,13 @@ importers: version: link:../packages/vite-plugin-runtime '@storybook/addon-docs': specifier: 'catalog:' - version: 10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) '@storybook/addon-themes': specifier: 'catalog:' - version: 10.5.6(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + version: 10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) '@storybook/react-vite': specifier: 'catalog:' - version: 10.5.6(esbuild@0.28.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 10.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) '@tailwindcss/vite': specifier: 'catalog:' version: 4.3.3(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) @@ -1398,7 +1398,7 @@ importers: version: 0.28.2 storybook: specifier: 'catalog:' - version: 10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) vite: specifier: 'catalog:' version: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) @@ -1912,10 +1912,6 @@ packages: resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.29.7': - resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} - engines: {node: '>=6.9.0'} - '@babel/generator@7.29.8': resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} engines: {node: '>=6.9.0'} @@ -2297,8 +2293,8 @@ packages: '@emmetio/stream-reader@2.2.0': resolution: {integrity: sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==} - '@emnapi/core@1.10.0': - resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + '@emnapi/core@1.11.0': + resolution: {integrity: sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==} '@emnapi/core@1.11.2': resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} @@ -2309,6 +2305,9 @@ packages: '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@emnapi/runtime@1.11.0': + resolution: {integrity: sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==} + '@emnapi/runtime@1.11.2': resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} @@ -3290,12 +3289,6 @@ packages: cpu: [x64] os: [win32] - '@napi-rs/wasm-runtime@1.1.4': - resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} - peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 - '@napi-rs/wasm-runtime@1.2.4': resolution: {integrity: sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==} engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} @@ -3750,8 +3743,8 @@ packages: '@oxc-project/types@0.150.0': resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==} - '@oxc-resolver/binding-android-arm-eabi@11.20.0': - resolution: {integrity: sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==} + '@oxc-resolver/binding-android-arm-eabi@11.21.2': + resolution: {integrity: sha512-xQoCRv+gKax9KTdwdaQNnAFOai8neay7g3jExDIORzhbrejwGSJaZNTdOJHR5ziLg2joMxOCMOFMo4zuxza2uQ==} cpu: [arm] os: [android] @@ -3760,8 +3753,8 @@ packages: cpu: [arm] os: [android] - '@oxc-resolver/binding-android-arm64@11.20.0': - resolution: {integrity: sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==} + '@oxc-resolver/binding-android-arm64@11.21.2': + resolution: {integrity: sha512-HF5oiE2L05yInPYCFD/4uxSrEZW4SuIfn99Y6L1xnJnzl066JR+MJs2rIdstw8A2MPlAKH+13dpFPNycjqzvGg==} cpu: [arm64] os: [android] @@ -3770,8 +3763,8 @@ packages: cpu: [arm64] os: [android] - '@oxc-resolver/binding-darwin-arm64@11.20.0': - resolution: {integrity: sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==} + '@oxc-resolver/binding-darwin-arm64@11.21.2': + resolution: {integrity: sha512-UX4u49CVCAD8QZNELaW8eMGgMAGwFWYEPbvNsh+3r/gs4NX3KfpiACMVwRQT0EuH3uat9hM5Zl+Ppm9pJD8tgg==} cpu: [arm64] os: [darwin] @@ -3780,8 +3773,8 @@ packages: cpu: [arm64] os: [darwin] - '@oxc-resolver/binding-darwin-x64@11.20.0': - resolution: {integrity: sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==} + '@oxc-resolver/binding-darwin-x64@11.21.2': + resolution: {integrity: sha512-J9xPx7YBkrRmJ+xl561ztnMWEc1aOyjEIxBiGX1dVb3u7bGSnfObfcZk+Pd+uM0HZAPNsQ1xvD8j52A/uOSqNQ==} cpu: [x64] os: [darwin] @@ -3790,8 +3783,8 @@ packages: cpu: [x64] os: [darwin] - '@oxc-resolver/binding-freebsd-x64@11.20.0': - resolution: {integrity: sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==} + '@oxc-resolver/binding-freebsd-x64@11.21.2': + resolution: {integrity: sha512-fRlt7OvSaQkWj6+EDTVxawVxOlqJB2QSnBfkeCyK4RTvsGctbw3BiH2Tb7DzMs7bikc4BRBpvWP5zF9K8b54Zg==} cpu: [x64] os: [freebsd] @@ -3800,8 +3793,8 @@ packages: cpu: [x64] os: [freebsd] - '@oxc-resolver/binding-linux-arm-gnueabihf@11.20.0': - resolution: {integrity: sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==} + '@oxc-resolver/binding-linux-arm-gnueabihf@11.21.2': + resolution: {integrity: sha512-gK+vPUcPQITkGwBKpZGrcDHSlU6eDGl7AQacxS2CEKAZIBHWkOVFeJwLZ4tYnA1acJqRM5lt7yYwPCVqGHIJ7A==} cpu: [arm] os: [linux] @@ -3810,8 +3803,8 @@ packages: cpu: [arm] os: [linux] - '@oxc-resolver/binding-linux-arm-musleabihf@11.20.0': - resolution: {integrity: sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==} + '@oxc-resolver/binding-linux-arm-musleabihf@11.21.2': + resolution: {integrity: sha512-L/Rgas7SrOKy/z7IH+HxSFRqVO4PuLDKLEGKvnhoCBBq3UJ0YzGBou3qMzaAGgkJwsIvX2pBF+7ojzfUhLiZxQ==} cpu: [arm] os: [linux] @@ -3820,8 +3813,8 @@ packages: cpu: [arm] os: [linux] - '@oxc-resolver/binding-linux-arm64-gnu@11.20.0': - resolution: {integrity: sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==} + '@oxc-resolver/binding-linux-arm64-gnu@11.21.2': + resolution: {integrity: sha512-CUEYvlX1Fk7E9kUMzuswru1J9HLxMwnpDeQGjQuI4ZH+iNCoa2X9T+pvyzrbsgl7WnIeTFTlNlHsRfVdf5g9/g==} cpu: [arm64] os: [linux] libc: [glibc] @@ -3832,8 +3825,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-arm64-musl@11.20.0': - resolution: {integrity: sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==} + '@oxc-resolver/binding-linux-arm64-musl@11.21.2': + resolution: {integrity: sha512-ViN1ZibQyxwC67GpoP33oo+S9UyUnkog13vzQb9+v9bCNvrVzJuk0MRdacjtv/9xfRMVF/eqHFyl8YOeykI10Q==} cpu: [arm64] os: [linux] libc: [musl] @@ -3844,8 +3837,8 @@ packages: os: [linux] libc: [musl] - '@oxc-resolver/binding-linux-ppc64-gnu@11.20.0': - resolution: {integrity: sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==} + '@oxc-resolver/binding-linux-ppc64-gnu@11.21.2': + resolution: {integrity: sha512-CU1sCqWnhGqYD5I1HedHk5pujrn7ssDkNB/AEQd/pd3D/EojVSgJUlpbafwIbyhia3PgIfkvdFpRPWSALzVumA==} cpu: [ppc64] os: [linux] libc: [glibc] @@ -3856,8 +3849,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-riscv64-gnu@11.20.0': - resolution: {integrity: sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==} + '@oxc-resolver/binding-linux-riscv64-gnu@11.21.2': + resolution: {integrity: sha512-jWVyZtIHca4Gb96x7dag+y69vlei7ffjrsveLkmf2ZhqEAz6ZSBnY1GWvgZUaZlwZP62A3xD092BW97Q5VGc+g==} cpu: [riscv64] os: [linux] libc: [glibc] @@ -3868,8 +3861,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-riscv64-musl@11.20.0': - resolution: {integrity: sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==} + '@oxc-resolver/binding-linux-riscv64-musl@11.21.2': + resolution: {integrity: sha512-LF29obFqNgBUgDX7rmUK7M4D0JQG5LxhYzn3xXmECcHU9aQAdWG7NiY052qybtesEdwHQXKNTWYQ7mTsybNvWg==} cpu: [riscv64] os: [linux] libc: [musl] @@ -3880,8 +3873,8 @@ packages: os: [linux] libc: [musl] - '@oxc-resolver/binding-linux-s390x-gnu@11.20.0': - resolution: {integrity: sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==} + '@oxc-resolver/binding-linux-s390x-gnu@11.21.2': + resolution: {integrity: sha512-+NYcm+cCHBbtdQQ3A4phQTSuVRYnNHz7wrl9XRAPEovcdoqi0mb1K5ZOl+jN54ZD+q1zz3V0vltbFJmzecJKmw==} cpu: [s390x] os: [linux] libc: [glibc] @@ -3892,8 +3885,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-x64-gnu@11.20.0': - resolution: {integrity: sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==} + '@oxc-resolver/binding-linux-x64-gnu@11.21.2': + resolution: {integrity: sha512-UQqZDdG2r2HhAOsZEgufkIWHPQ886IUyuJQkoZByvzhW8j51R4UNzGBJFkTiTnLhQnggwJRdJgFWK4uY6ZVIMw==} cpu: [x64] os: [linux] libc: [glibc] @@ -3904,8 +3897,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-x64-musl@11.20.0': - resolution: {integrity: sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==} + '@oxc-resolver/binding-linux-x64-musl@11.21.2': + resolution: {integrity: sha512-3Q9PMRjalWkT6NZ4jfujuqTCFwoWErg3y3BnOgb544B8IMw4PktiwWOigMfOHNRLMghZeJ7hpfpZf4CP7rV7Og==} cpu: [x64] os: [linux] libc: [musl] @@ -3916,8 +3909,8 @@ packages: os: [linux] libc: [musl] - '@oxc-resolver/binding-openharmony-arm64@11.20.0': - resolution: {integrity: sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==} + '@oxc-resolver/binding-openharmony-arm64@11.21.2': + resolution: {integrity: sha512-Eljeq3ndtyKhM+Es8LITi4Zl2htzuRZcrPMF3kMCsrILztvU6AjZ3FuEhHHKobPUB9rMpzLpJP5bDqXI7r+iog==} cpu: [arm64] os: [openharmony] @@ -3926,8 +3919,8 @@ packages: cpu: [arm64] os: [openharmony] - '@oxc-resolver/binding-wasm32-wasi@11.20.0': - resolution: {integrity: sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==} + '@oxc-resolver/binding-wasm32-wasi@11.21.2': + resolution: {integrity: sha512-HGDbNsIywqc4LxU38+CTJNnB/6BF7rheWOJ259b4eE0aEnelYblC8x+1tEd63bp31fYne63RQz9Jb4yVnC7Yig==} engines: {node: '>=14.0.0'} cpu: [wasm32] @@ -3936,8 +3929,8 @@ packages: engines: {node: '>=14.0.0'} cpu: [wasm32] - '@oxc-resolver/binding-win32-arm64-msvc@11.20.0': - resolution: {integrity: sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==} + '@oxc-resolver/binding-win32-arm64-msvc@11.21.2': + resolution: {integrity: sha512-iWx25CBEgH49iE9q5coEGI/jb1jl5kkCY9z6U5Og67xCkQ/WFMDc2J5U78+AE91SUxM2NqSLhJC8/PLfWnImww==} cpu: [arm64] os: [win32] @@ -3946,8 +3939,8 @@ packages: cpu: [arm64] os: [win32] - '@oxc-resolver/binding-win32-x64-msvc@11.20.0': - resolution: {integrity: sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==} + '@oxc-resolver/binding-win32-x64-msvc@11.21.2': + resolution: {integrity: sha512-VPoCAhKvCQTlG7vxqaBXcmuvbh77BfnGXj8g0pbvVXpm1F/R8rDVwqIWcEbMzrI1JvJlm8v7T9uMVQb6UctMRg==} cpu: [x64] os: [win32] @@ -5241,44 +5234,26 @@ packages: '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - '@storybook/addon-docs@10.5.6': - resolution: {integrity: sha512-zyUJBrrpC9NTrmsREaVFNr+9WW6pikJtmRvo7GgZGqthEyhjQKSarHrW0aNWkwae2ep3jp1CZi8vIUVG1Dnp0w==} + '@storybook/addon-docs@10.6.1': + resolution: {integrity: sha512-31PXR+MCWfI5YHKKah6tWnbV1FJWA9qbTHxtvpszPLd8FBekXtPOnmyYOpFF9CfciBF9bkeJFdFanJvUCjWbjA==} peerDependencies: '@types/react': '*' - storybook: ^10.5.6 + storybook: ^10.6.1 peerDependenciesMeta: '@types/react': optional: true - '@storybook/addon-themes@10.5.6': - resolution: {integrity: sha512-fIzu2f6xPh/mtOBUU9TnjSoO3qUIF57AVES9HX4dfhjtbz9lWd+EhNd65nZfmTXxKsS32x/7/aXDTHtuP/ObwQ==} + '@storybook/addon-themes@10.6.1': + resolution: {integrity: sha512-SVZSM+mIiyK56d/3mRpRwPkAlpr9fdSSz/XpkT6fzI1PYFWDGSdElu2YmauzhaFGnlmAKL//tmXVvHXD4qCkIQ==} peerDependencies: - storybook: ^10.5.6 + storybook: ^10.6.1 - '@storybook/builder-vite@10.5.6': - resolution: {integrity: sha512-Ts8EohKPj8okDPCkueeKVN+IRGNpI3LuddsFGupqraRvK6aRWawDKA28uc0PlsLCLWbMkMsGVw+IpFXfmoLJgQ==} + '@storybook/builder-vite@10.6.1': + resolution: {integrity: sha512-kUMRRRS4MpJdxOLhrlQX3cpMYi2vVB3tIIRXyqWmAjVM6xT7OZ1SCQEMFPP+S3ToRqaYWZQb8aBqrJucbbYufA==} peerDependencies: - storybook: ^10.5.6 + storybook: ^10.6.1 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - '@storybook/csf-plugin@10.5.6': - resolution: {integrity: sha512-PJLyOmcKe1OZDBw7RaGX/gjuiJuVfS5pVgc4W2RnHYOFpU6F5Bv9+9MqQwp0i7tWZBWc4fsCJudgVqwgjuTROA==} - peerDependencies: - esbuild: '*' - rollup: '*' - storybook: ^10.5.6 - vite: '*' - webpack: '*' - peerDependenciesMeta: - esbuild: - optional: true - rollup: - optional: true - vite: - optional: true - webpack: - optional: true - '@storybook/global@5.0.0': resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} @@ -5288,40 +5263,40 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@storybook/react-dom-shim@10.5.6': - resolution: {integrity: sha512-dV3oOHc5ImggxEqeIiUj4vvnQO5SKScFtqAkxgIWLju1wiSZSIqQ5Q4Mp12Rhs9hQrjF039DueH7f2xJJZfvSw==} + '@storybook/react-dom-shim@10.6.1': + resolution: {integrity: sha512-OkPFycR6kVE+9ZsgQWRqnUfZm/sLY6dxsNAn3lsvV7DSAQWH9QnHLkqP9E/N/Hg6cKPDtGzDZ8vPNDz2FuaDuQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.5.6 + storybook: ^10.6.1 peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - '@storybook/react-vite@10.5.6': - resolution: {integrity: sha512-DCTfNZWhQUH4Zf8LDE4zdLn6+C26QNW0KETdnFUkdrqRADlwS6oExtGWC5f/uP/GDbKb9jrGbC+/ap8nWEH/vQ==} + '@storybook/react-vite@10.6.1': + resolution: {integrity: sha512-oyUZxuW2lIJN8BRbYyMyZbfjeYqtF6HwmNfqa1SAF525zRnMewYp66wm/5d8FAgwqejOf6N2+wR3hX866lSwMA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.5.6 + storybook: ^10.6.1 typescript: 6.0.x vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - '@storybook/react@10.5.6': - resolution: {integrity: sha512-dXSdNoc9yAvpa4hiegQhmZPXOKunAxkPX94DxvRw/kM6+wujVFAGlZjYygKrWw357KOjPRK7SO1LRTc70mgrhQ==} + '@storybook/react@10.6.1': + resolution: {integrity: sha512-t0QIzO0MVcpD52UEMZKSv35nO/+8vUnLGoFC1E3BIVjHEIdr6IFFM7ty2E7FWsaZGsah3XrFINKT3dPd2Atz+Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.5.6 + storybook: ^10.6.1 typescript: 6.0.x peerDependenciesMeta: '@types/react': @@ -5679,8 +5654,8 @@ packages: '@types/react-dom': optional: true - '@testing-library/user-event@14.6.1': - resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + '@testing-library/user-event@14.6.7': + resolution: {integrity: sha512-MPCpX8bxe8zS+JmmTwLp8jd0dy1rAm60Te/SL8JrQM3qvQJcBOs1d7IefJMyZzqM3EWBrDn/LWDt1BCGu4ASfg==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' @@ -5725,9 +5700,6 @@ packages: cpu: [arm64] os: [win32] - '@tybys/wasm-util@0.10.2': - resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} - '@tybys/wasm-util@0.10.4': resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} @@ -8958,8 +8930,8 @@ packages: resolution: {integrity: sha512-zwajKw1GUa57IOdafIS7/yOObGF2NihwIfZfs5yJTRprMSkiwhWOc/k+n8bwnUPbHr2/XsjfBO8KyTPaW3VpWg==} engines: {node: ^20.19.0 || >=22.12.0} - oxc-resolver@11.20.0: - resolution: {integrity: sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==} + oxc-resolver@11.21.2: + resolution: {integrity: sha512-w5tLwYN3Zo24w5EeWJjJWZOwhYqTtC8PS2B1tIt7BZUuqTIcU07sQValbDw+rq7+AuAGzOHklgK+ifsy4lpXfw==} oxc-resolver@11.24.2: resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} @@ -9982,8 +9954,8 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} - storybook@10.5.6: - resolution: {integrity: sha512-VhYwqxPySa24CVXKoWD6gCZXx9//DTmo43YpusGuAoHDYj5Osjt8wuBRQVeGoaLUWnHiPWv8S+GYHrJEaBM6Rg==} + storybook@10.6.1: + resolution: {integrity: sha512-NeEmukMWPA5OqHizUkQtxCIhGt7MUZLPY4FFgpC0LNiipjrac/oKpH0TxI+9Zc+vI5+IoN+NiskOmBP7gtYfmA==} hasBin: true peerDependencies: '@types/react': '*' @@ -11414,7 +11386,7 @@ snapshots: '@babel/core@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) '@babel/helpers': 7.29.7 @@ -11431,14 +11403,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.29.7': - dependencies: - '@babel/parser': 7.29.9 - '@babel/types': 7.29.8 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - '@babel/generator@7.29.8': dependencies: '@babel/parser': 7.29.9 @@ -11499,7 +11463,7 @@ snapshots: '@babel/traverse@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/helper-globals': 7.29.7 '@babel/parser': 7.29.9 '@babel/template': 7.29.7 @@ -11619,7 +11583,7 @@ snapshots: '@commitlint/is-ignored@21.2.3': dependencies: '@commitlint/types': 21.2.3 - semver: 7.8.1 + semver: 7.8.5 '@commitlint/lint@21.2.3': dependencies: @@ -11691,7 +11655,7 @@ snapshots: dependencies: '@simple-libs/child-process-utils': 2.0.0 '@simple-libs/stream-utils': 2.0.0 - semver: 7.8.1 + semver: 7.8.5 optionalDependencies: conventional-commits-parser: 7.1.2 @@ -11999,9 +11963,9 @@ snapshots: '@emmetio/stream-reader@2.2.0': {} - '@emnapi/core@1.10.0': + '@emnapi/core@1.11.0': dependencies: - '@emnapi/wasi-threads': 1.2.1 + '@emnapi/wasi-threads': 1.2.2 tslib: 2.8.1 optional: true @@ -12022,6 +11986,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.0': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.11.2': dependencies: tslib: 2.8.1 @@ -12585,7 +12554,7 @@ snapshots: '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/remapping@2.3.5': @@ -12602,7 +12571,7 @@ snapshots: '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 '@jspsych/plugin-html-button-response@1.2.0(jspsych@7.3.4)': dependencies: @@ -12791,18 +12760,11 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': optional: true - '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@napi-rs/wasm-runtime@1.2.4(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0)': dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.2 - optional: true - - '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)': - dependencies: - '@emnapi/core': 1.9.2 - '@emnapi/runtime': 1.9.2 - '@tybys/wasm-util': 0.10.2 + '@emnapi/core': 1.11.0 + '@emnapi/runtime': 1.11.0 + '@tybys/wasm-util': 0.10.4 optional: true '@napi-rs/wasm-runtime@1.2.4(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': @@ -12812,6 +12774,13 @@ snapshots: '@tybys/wasm-util': 0.10.4 optional: true + '@napi-rs/wasm-runtime@1.2.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)': + dependencies: + '@emnapi/core': 1.9.2 + '@emnapi/runtime': 1.9.2 + '@tybys/wasm-util': 0.10.4 + optional: true + '@nestjs/axios@12.0.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.20.0)(rxjs@7.8.2)': dependencies: '@nestjs/common': 12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -13050,7 +13019,7 @@ snapshots: dependencies: '@emnapi/core': 1.9.2 '@emnapi/runtime': 1.9.2 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) + '@napi-rs/wasm-runtime': 1.2.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) optional: true '@oxc-parser/binding-win32-arm64-msvc@0.127.0': @@ -13075,107 +13044,107 @@ snapshots: '@oxc-project/types@0.150.0': {} - '@oxc-resolver/binding-android-arm-eabi@11.20.0': + '@oxc-resolver/binding-android-arm-eabi@11.21.2': optional: true '@oxc-resolver/binding-android-arm-eabi@11.24.2': optional: true - '@oxc-resolver/binding-android-arm64@11.20.0': + '@oxc-resolver/binding-android-arm64@11.21.2': optional: true '@oxc-resolver/binding-android-arm64@11.24.2': optional: true - '@oxc-resolver/binding-darwin-arm64@11.20.0': + '@oxc-resolver/binding-darwin-arm64@11.21.2': optional: true '@oxc-resolver/binding-darwin-arm64@11.24.2': optional: true - '@oxc-resolver/binding-darwin-x64@11.20.0': + '@oxc-resolver/binding-darwin-x64@11.21.2': optional: true '@oxc-resolver/binding-darwin-x64@11.24.2': optional: true - '@oxc-resolver/binding-freebsd-x64@11.20.0': + '@oxc-resolver/binding-freebsd-x64@11.21.2': optional: true '@oxc-resolver/binding-freebsd-x64@11.24.2': optional: true - '@oxc-resolver/binding-linux-arm-gnueabihf@11.20.0': + '@oxc-resolver/binding-linux-arm-gnueabihf@11.21.2': optional: true '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2': optional: true - '@oxc-resolver/binding-linux-arm-musleabihf@11.20.0': + '@oxc-resolver/binding-linux-arm-musleabihf@11.21.2': optional: true '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2': optional: true - '@oxc-resolver/binding-linux-arm64-gnu@11.20.0': + '@oxc-resolver/binding-linux-arm64-gnu@11.21.2': optional: true '@oxc-resolver/binding-linux-arm64-gnu@11.24.2': optional: true - '@oxc-resolver/binding-linux-arm64-musl@11.20.0': + '@oxc-resolver/binding-linux-arm64-musl@11.21.2': optional: true '@oxc-resolver/binding-linux-arm64-musl@11.24.2': optional: true - '@oxc-resolver/binding-linux-ppc64-gnu@11.20.0': + '@oxc-resolver/binding-linux-ppc64-gnu@11.21.2': optional: true '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2': optional: true - '@oxc-resolver/binding-linux-riscv64-gnu@11.20.0': + '@oxc-resolver/binding-linux-riscv64-gnu@11.21.2': optional: true '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2': optional: true - '@oxc-resolver/binding-linux-riscv64-musl@11.20.0': + '@oxc-resolver/binding-linux-riscv64-musl@11.21.2': optional: true '@oxc-resolver/binding-linux-riscv64-musl@11.24.2': optional: true - '@oxc-resolver/binding-linux-s390x-gnu@11.20.0': + '@oxc-resolver/binding-linux-s390x-gnu@11.21.2': optional: true '@oxc-resolver/binding-linux-s390x-gnu@11.24.2': optional: true - '@oxc-resolver/binding-linux-x64-gnu@11.20.0': + '@oxc-resolver/binding-linux-x64-gnu@11.21.2': optional: true '@oxc-resolver/binding-linux-x64-gnu@11.24.2': optional: true - '@oxc-resolver/binding-linux-x64-musl@11.20.0': + '@oxc-resolver/binding-linux-x64-musl@11.21.2': optional: true '@oxc-resolver/binding-linux-x64-musl@11.24.2': optional: true - '@oxc-resolver/binding-openharmony-arm64@11.20.0': + '@oxc-resolver/binding-openharmony-arm64@11.21.2': optional: true '@oxc-resolver/binding-openharmony-arm64@11.24.2': optional: true - '@oxc-resolver/binding-wasm32-wasi@11.20.0': + '@oxc-resolver/binding-wasm32-wasi@11.21.2': dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@emnapi/core': 1.11.0 + '@emnapi/runtime': 1.11.0 + '@napi-rs/wasm-runtime': 1.2.4(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0) optional: true '@oxc-resolver/binding-wasm32-wasi@11.24.2': @@ -13185,13 +13154,13 @@ snapshots: '@napi-rs/wasm-runtime': 1.2.4(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true - '@oxc-resolver/binding-win32-arm64-msvc@11.20.0': + '@oxc-resolver/binding-win32-arm64-msvc@11.21.2': optional: true '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': optional: true - '@oxc-resolver/binding-win32-x64-msvc@11.20.0': + '@oxc-resolver/binding-win32-x64-msvc@11.21.2': optional: true '@oxc-resolver/binding-win32-x64-msvc@11.24.2': @@ -14721,67 +14690,35 @@ snapshots: '@standard-schema/utils@0.3.0': {} - '@storybook/addon-docs@10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@storybook/addon-docs@10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': dependencies: '@mdx-js/react': 3.1.1(react@18.3.1) - '@storybook/csf-plugin': 10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) '@storybook/icons': 2.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/react-dom-shim': 10.5.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + '@storybook/react-dom-shim': 10.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - storybook: 10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) ts-dedent: 2.2.0 + unplugin: 2.3.11 transitivePeerDependencies: - '@types/react-dom' - - esbuild - - rollup - - vite - - webpack - '@storybook/addon-themes@10.5.6(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + '@storybook/addon-themes@10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': dependencies: - storybook: 10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) ts-dedent: 2.2.0 - '@storybook/builder-vite@10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@storybook/builder-vite@10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: - '@storybook/csf-plugin': 10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) - storybook: 10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) ts-dedent: 2.2.0 vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) - transitivePeerDependencies: - - esbuild - - rollup - - webpack - '@storybook/builder-vite@10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@storybook/builder-vite@10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: - '@storybook/csf-plugin': 10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) - storybook: 10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) + storybook: 10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) ts-dedent: 2.2.0 vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) - transitivePeerDependencies: - - esbuild - - rollup - - webpack - - '@storybook/csf-plugin@10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': - dependencies: - storybook: 10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - unplugin: 2.3.11 - optionalDependencies: - esbuild: 0.28.2 - rollup: 4.61.1 - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) - - '@storybook/csf-plugin@10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': - dependencies: - storybook: 10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) - unplugin: 2.3.11 - optionalDependencies: - esbuild: 0.28.2 - rollup: 4.61.1 - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) '@storybook/global@5.0.0': {} @@ -14800,37 +14737,37 @@ snapshots: react: link:vendor/react@19.x react-dom: link:vendor/react-dom@19.x - '@storybook/react-dom-shim@10.5.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + '@storybook/react-dom-shim@10.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - storybook: 10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@storybook/react-dom-shim@10.5.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + '@storybook/react-dom-shim@10.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': dependencies: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - storybook: 10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@storybook/react-dom-shim@10.5.6(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))': + '@storybook/react-dom-shim@10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))': dependencies: react: link:vendor/react@19.x react-dom: link:vendor/react-dom@19.x - storybook: 10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) + storybook: 10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) - '@storybook/react-vite@10.5.6(esbuild@0.28.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@storybook/react-vite@10.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) '@rollup/pluginutils': 5.4.0(rollup@4.61.1) - '@storybook/builder-vite': 10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) - '@storybook/react': 10.5.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3) + '@storybook/builder-vite': 10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + '@storybook/react': 10.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3) empathic: 2.0.1 - magic-string: 0.30.21 + magic-string: 1.4.2 react: 19.1.0 react-docgen: 8.0.3 react-dom: 19.1.0(react@19.1.0) resolve: 1.22.12 - storybook: 10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) tsconfig-paths: 4.2.0 vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) optionalDependencies: @@ -14838,24 +14775,22 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@types/react-dom' - - esbuild - rollup - supports-color - - webpack - '@storybook/react-vite@10.5.6(esbuild@0.28.2)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@storybook/react-vite@10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) '@rollup/pluginutils': 5.4.0(rollup@4.61.1) - '@storybook/builder-vite': 10.5.6(esbuild@0.28.2)(rollup@4.61.1)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) - '@storybook/react': 10.5.6(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3) + '@storybook/builder-vite': 10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + '@storybook/react': 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3) empathic: 2.0.1 - magic-string: 0.30.21 + magic-string: 1.4.2 react: link:vendor/react@19.x react-docgen: 8.0.3 react-dom: link:vendor/react-dom@19.x resolve: 1.22.12 - storybook: 10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) + storybook: 10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) tsconfig-paths: 4.2.0 vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) optionalDependencies: @@ -14863,34 +14798,32 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@types/react-dom' - - esbuild - rollup - supports-color - - webpack - '@storybook/react@10.5.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3)': + '@storybook/react@10.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.5.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + '@storybook/react-dom-shim': 10.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) react: 19.1.0 react-docgen: 8.0.3 react-docgen-typescript: 2.4.0(typescript@6.0.3) react-dom: 19.1.0(react@19.1.0) - storybook: 10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) optionalDependencies: typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@storybook/react@10.5.6(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)': + '@storybook/react@10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.5.6(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)) + '@storybook/react-dom-shim': 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)) react: link:vendor/react@19.x react-docgen: 8.0.3 react-docgen-typescript: 2.4.0(typescript@6.0.3) react-dom: link:vendor/react-dom@19.x - storybook: 10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) + storybook: 10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) optionalDependencies: typescript: 6.0.3 transitivePeerDependencies: @@ -15243,7 +15176,7 @@ snapshots: react: link:vendor/react@19.x react-dom: link:vendor/react-dom@19.x - '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.2)': + '@testing-library/user-event@14.6.7(@testing-library/dom@10.4.2)': dependencies: '@testing-library/dom': 10.4.2 @@ -15280,11 +15213,6 @@ snapshots: '@turbo/windows-arm64@2.11.5': optional: true - '@tybys/wasm-util@0.10.2': - dependencies: - tslib: 2.8.1 - optional: true - '@tybys/wasm-util@0.10.4': dependencies: tslib: 2.8.1 @@ -15564,7 +15492,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.60.1 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.1 + semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 @@ -15923,7 +15851,7 @@ snapshots: espree: 10.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 - semver: 7.8.1 + semver: 7.8.5 transitivePeerDependencies: - supports-color @@ -16975,12 +16903,12 @@ snapshots: eslint-compat-utils@0.5.1(eslint@9.39.5(jiti@2.7.0)): dependencies: eslint: 9.39.5(jiti@2.7.0) - semver: 7.8.1 + semver: 7.8.5 eslint-compat-utils@0.6.5(eslint@9.39.5(jiti@2.7.0)): dependencies: eslint: 9.39.5(jiti@2.7.0) - semver: 7.8.1 + semver: 7.8.5 eslint-import-resolver-node@0.3.10: dependencies: @@ -18251,7 +18179,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.8.1 + semver: 7.8.5 jspsych@7.3.4: dependencies: @@ -18457,11 +18385,11 @@ snapshots: magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 magic-string@0.30.21: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 magic-string@1.4.2: dependencies: @@ -19353,27 +19281,27 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc': 0.150.0 '@oxc-parser/binding-win32-x64-msvc': 0.150.0 - oxc-resolver@11.20.0: + oxc-resolver@11.21.2: optionalDependencies: - '@oxc-resolver/binding-android-arm-eabi': 11.20.0 - '@oxc-resolver/binding-android-arm64': 11.20.0 - '@oxc-resolver/binding-darwin-arm64': 11.20.0 - '@oxc-resolver/binding-darwin-x64': 11.20.0 - '@oxc-resolver/binding-freebsd-x64': 11.20.0 - '@oxc-resolver/binding-linux-arm-gnueabihf': 11.20.0 - '@oxc-resolver/binding-linux-arm-musleabihf': 11.20.0 - '@oxc-resolver/binding-linux-arm64-gnu': 11.20.0 - '@oxc-resolver/binding-linux-arm64-musl': 11.20.0 - '@oxc-resolver/binding-linux-ppc64-gnu': 11.20.0 - '@oxc-resolver/binding-linux-riscv64-gnu': 11.20.0 - '@oxc-resolver/binding-linux-riscv64-musl': 11.20.0 - '@oxc-resolver/binding-linux-s390x-gnu': 11.20.0 - '@oxc-resolver/binding-linux-x64-gnu': 11.20.0 - '@oxc-resolver/binding-linux-x64-musl': 11.20.0 - '@oxc-resolver/binding-openharmony-arm64': 11.20.0 - '@oxc-resolver/binding-wasm32-wasi': 11.20.0 - '@oxc-resolver/binding-win32-arm64-msvc': 11.20.0 - '@oxc-resolver/binding-win32-x64-msvc': 11.20.0 + '@oxc-resolver/binding-android-arm-eabi': 11.21.2 + '@oxc-resolver/binding-android-arm64': 11.21.2 + '@oxc-resolver/binding-darwin-arm64': 11.21.2 + '@oxc-resolver/binding-darwin-x64': 11.21.2 + '@oxc-resolver/binding-freebsd-x64': 11.21.2 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.21.2 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.21.2 + '@oxc-resolver/binding-linux-arm64-gnu': 11.21.2 + '@oxc-resolver/binding-linux-arm64-musl': 11.21.2 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.21.2 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.21.2 + '@oxc-resolver/binding-linux-riscv64-musl': 11.21.2 + '@oxc-resolver/binding-linux-s390x-gnu': 11.21.2 + '@oxc-resolver/binding-linux-x64-gnu': 11.21.2 + '@oxc-resolver/binding-linux-x64-musl': 11.21.2 + '@oxc-resolver/binding-openharmony-arm64': 11.21.2 + '@oxc-resolver/binding-wasm32-wasi': 11.21.2 + '@oxc-resolver/binding-win32-arm64-msvc': 11.21.2 + '@oxc-resolver/binding-win32-x64-msvc': 11.21.2 oxc-resolver@11.24.2: optionalDependencies: @@ -20546,23 +20474,23 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 - storybook@10.5.6(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@testing-library/dom': 10.4.2 '@testing-library/jest-dom': 6.9.1 - '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.2) + '@testing-library/user-event': 14.6.7(@testing-library/dom@10.4.2) '@vitest/expect': 3.2.4 '@vitest/spy': 3.2.4 '@webcontainer/env': 1.1.1 - esbuild: 0.27.7 + esbuild: 0.28.2 jsonc-parser: 3.3.1 open: 10.2.0 oxc-parser: 0.127.0 - oxc-resolver: 11.20.0 + oxc-resolver: 11.21.2 recast: 0.23.11 - semver: 7.8.1 + semver: 7.8.5 use-sync-external-store: 1.6.0(react@19.1.0) ws: 8.22.0 optionalDependencies: @@ -20573,23 +20501,23 @@ snapshots: - react-dom - utf-8-validate - storybook@10.5.6(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x): + storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x): dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.0.2(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) '@testing-library/dom': 10.4.2 '@testing-library/jest-dom': 6.9.1 - '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.2) + '@testing-library/user-event': 14.6.7(@testing-library/dom@10.4.2) '@vitest/expect': 3.2.4 '@vitest/spy': 3.2.4 '@webcontainer/env': 1.1.1 - esbuild: 0.27.7 + esbuild: 0.28.2 jsonc-parser: 3.3.1 open: 10.2.0 oxc-parser: 0.127.0 - oxc-resolver: 11.20.0 + oxc-resolver: 11.21.2 recast: 0.23.11 - semver: 7.8.1 + semver: 7.8.5 use-sync-external-store: 1.6.0(react@vendor+react@19.x) ws: 8.22.0 optionalDependencies: @@ -20997,7 +20925,7 @@ snapshots: typescript-auto-import-cache@0.3.6: dependencies: - semver: 7.8.1 + semver: 7.8.5 typescript-eslint@8.60.1(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3): dependencies: @@ -21356,7 +21284,7 @@ snapshots: volar-service-typescript@0.0.70(@volar/language-service@2.4.28): dependencies: path-browserify: 1.0.1 - semver: 7.8.1 + semver: 7.8.5 typescript-auto-import-cache: 0.3.6 vscode-languageserver-textdocument: 1.0.12 vscode-nls: 5.2.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1f97543df..9582b56eb 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -46,15 +46,21 @@ catalog: serialize-error: '^13.0.1' vite: '^7.3.6' zustand: ^5.0.15 - '@storybook/addon-docs': '10.5.6' - '@storybook/addon-themes': '10.5.6' - '@storybook/react-vite': '10.5.6' - 'storybook': '10.5.6' + '@storybook/addon-docs': '^10.6.1' + '@storybook/addon-themes': '^10.6.1' + '@storybook/react-vite': '^10.6.1' + 'storybook': '^10.6.1' catalogs: react19: react: 19.1.0 react-dom: 19.1.0 react-is: 19.1.0 +# storybook's published types import @storybook/react without depending on it. Hoisted, that import +# reaches a different peer variant than the stories use, TypeScript merges the two copies onto it, +# and every story's args become `any` (storybook/AGENTS.md). +hoistPattern: + - '*' + - '!@storybook/react' minimumReleaseAge: 10080 # 7 days minimumReleaseAgeExclude: - '@douglasneuroinformatics/*' diff --git a/storybook/AGENTS.md b/storybook/AGENTS.md index 8b38b3f63..720e8225f 100644 --- a/storybook/AGENTS.md +++ b/storybook/AGENTS.md @@ -21,6 +21,24 @@ Note the asymmetry: `apps/web` is scanned from `src`, the other two only from `s `apps/gateway` and every other package are not scanned at all. Covering a new location means adding an entry to the `stories` array. +## `@storybook/react` must not be hoisted, or every story's `args` lose their types + +`pnpm-workspace.yaml` sets `hoistPattern: ['*', '!@storybook/react']`. Since Storybook 10.5.7 the +`storybook` package's own declarations import `@storybook/react` without depending on it (#1619). +Hoisted, that import resolves to `node_modules/.pnpm/node_modules/@storybook/react`, the peer +variant installed for this workspace's real `react`, which has no types: `react` ships none and +root `package.json` deletes `@types/react` (`vendor/AGENTS.md`). The stories reach a second variant, +paired with `vendor/react@19.x`, through `@storybook/react-vite`. TypeScript treats copies with the +same name and version as one package and keeps the first it loads, so every story is checked +against the copy without React types: `ComponentProps` does not resolve and +`StoryObj` gives `args` no type. **Remove the exclusion and every story's `args` +become `any` without an error**; only a callback in `args` fails, with TS7006 in the owning +package's `tsc`. + +A checkout installed before the exclusion keeps its hoisted link: `pnpm install` records the new +pattern but reports "Already up to date" and leaves the link in place. Delete the root +`node_modules` and install again; `node_modules/.pnpm/node_modules/@storybook/react` must not exist. + ## The `@` alias resolves by importer, and only for two apps `vite.config.js` replaces the usual static alias with a `customResolver` that maps `@` to From bb760ef8f8db4e4f7e296ba49682852ad2074c37 Mon Sep 17 00:00:00 2001 From: joshunrau Date: Wed, 7 Oct 2026 11:03:36 -0400 Subject: [PATCH 11/14] build(outreach): upgrade typedoc to 0.28 outreach moves typedoc from 0.27.x to ^0.28.20 and typedoc-plugin-markdown from 4.4.x to ^4.13.1, the newest releases past the 7-day release age (#1623). The two move together because every plugin release from 4.5.0 on peers typedoc 0.28. typedoc 0.27.9 peered TypeScript only up to 5.8, so astro check warned about an unsupported TypeScript version on every lint. 0.28 peers 6.0 and the warning is gone. typedoc 0.28 replaces Reflection.url with a router, and the starlight-typedoc fork is ported the way upstream starlight-typedoc 0.21 did it. The sidebar builder reads each reflection's page URL from the pages typedoc reports when rendering ends. The theme overrides urlTo, which the plugin now calls for every link to a reflection, in place of getRelativeUrl, and inline link tags resolve through it as well. The pages are written by the plugin's markdown output, because generateDocs always writes the html output in 0.28. The page event handlers drop their Reflection annotation, which 0.28's events no longer accept. typedoc-plugin-markdown's member router puts a namespace exported from the project root under a directory named after the project, so the FormInstrument, FormTypes and other namespace pages would have moved to @opendatacapture/namespaces/ and dropped out of the sidebar. router.ts extends the member router to keep them at namespaces//. Every generated page keeps its URL and the sidebar is unchanged. The markdown differs only where the plugin renders differently: type parameters get their own headings, aliases read "= type", object aliases list their properties with source locations, and a type no longer links to the page it is on. Two inherited-member links into BaseTranslator follow the new headings. The constructor link now targets the Constructor heading rather than the Constructors section, and the link to the t method gains a -1 suffix, because the type parameter T now takes the plain anchor. Both resolve. The outreach AGENTS.md records the router override and what to diff after the next upgrade. Co-Authored-By: Claude Opus 5.5 --- apps/outreach/AGENTS.md | 8 ++ apps/outreach/package.json | 4 +- .../plugins/starlight-plugin-typedoc/index.ts | 3 +- .../starlight-plugin-typedoc/router.ts | 16 ++++ .../starlight-plugin-typedoc/starlight.ts | 42 ++++++--- .../plugins/starlight-plugin-typedoc/theme.ts | 12 +-- .../starlight-plugin-typedoc/typedoc.ts | 36 ++++++-- pnpm-lock.yaml | 89 +++++++------------ 8 files changed, 120 insertions(+), 90 deletions(-) create mode 100644 apps/outreach/src/plugins/starlight-plugin-typedoc/router.ts diff --git a/apps/outreach/AGENTS.md b/apps/outreach/AGENTS.md index 4b811308f..c937f1a4b 100644 --- a/apps/outreach/AGENTS.md +++ b/apps/outreach/AGENTS.md @@ -55,6 +55,14 @@ group there. `lib/index.d.ts` on every build, so hand edits are lost. To change that API reference, change runtime-core's TSDoc. Background: `.agents/docs/architecture/runtime-and-vendor.md`. +The plugin is a fork of upstream `starlight-typedoc`, built on typedoc's router: the sidebar comes from +the page URLs typedoc reports at `RendererEvent.END`, and `theme.ts` overrides `urlTo` to emit +absolute Starlight links. `router.ts` keeps namespaces exported from the package root at +`namespaces//`; typedoc-plugin-markdown's member router would nest them under +`@opendatacapture/`, which moves their URLs and drops the Namespaces sidebar group. After upgrading +typedoc or typedoc-plugin-markdown, build before and after and diff the generated directory, its +links and anchors, and the sidebar. + ## Content collections Config is `src/content/config.ts` — the legacy Astro path, **not** `src/content.config.ts`. The diff --git a/apps/outreach/package.json b/apps/outreach/package.json index 3e39b5de7..085800ee8 100644 --- a/apps/outreach/package.json +++ b/apps/outreach/package.json @@ -35,7 +35,7 @@ "github-slugger": "^2.0.0", "sharp": "^0.34.5", "tailwindcss": "catalog:", - "typedoc": "0.27.x", - "typedoc-plugin-markdown": "4.4.x" + "typedoc": "^0.28.20", + "typedoc-plugin-markdown": "^4.13.1" } } diff --git a/apps/outreach/src/plugins/starlight-plugin-typedoc/index.ts b/apps/outreach/src/plugins/starlight-plugin-typedoc/index.ts index eb47e903e..73f02d20b 100644 --- a/apps/outreach/src/plugins/starlight-plugin-typedoc/index.ts +++ b/apps/outreach/src/plugins/starlight-plugin-typedoc/index.ts @@ -47,12 +47,13 @@ export function starlightTypeDocPlugin(options: StarlightTypeDocOptions): Starli return { hooks: { async setup({ astroConfig, config, logger, updateConfig }) { - const { baseOutputDirectory, reflections } = await generateTypeDoc(options, astroConfig, logger); + const { baseOutputDirectory, pageUrls, reflections } = await generateTypeDoc(options, astroConfig, logger); const sidebar = getSidebarFromReflections( config.sidebar, starlightTypeDocSidebarGroup, options.sidebar, reflections, + pageUrls, baseOutputDirectory ); updateConfig({ sidebar }); diff --git a/apps/outreach/src/plugins/starlight-plugin-typedoc/router.ts b/apps/outreach/src/plugins/starlight-plugin-typedoc/router.ts new file mode 100644 index 000000000..bc6552e6b --- /dev/null +++ b/apps/outreach/src/plugins/starlight-plugin-typedoc/router.ts @@ -0,0 +1,16 @@ +import { ReflectionKind } from 'typedoc'; +import type { Reflection } from 'typedoc'; +import { MemberRouter } from 'typedoc-plugin-markdown'; + +/** + * typedoc-plugin-markdown's member router places a namespace exported from the project root under a directory + * named after the project (`@opendatacapture/namespaces/...`), unlike every other root export. + */ +export class StarlightTypeDocRouter extends MemberRouter { + override getIdealBaseName(reflection: Reflection): string { + if (reflection.kindOf(ReflectionKind.Namespace) && reflection.parent?.isProject()) { + return `namespaces/${this.getReflectionAlias(reflection)}/${this.entryFileName}`; + } + return super.getIdealBaseName(reflection); + } +} diff --git a/apps/outreach/src/plugins/starlight-plugin-typedoc/starlight.ts b/apps/outreach/src/plugins/starlight-plugin-typedoc/starlight.ts index 008609dbf..d429df3cd 100644 --- a/apps/outreach/src/plugins/starlight-plugin-typedoc/starlight.ts +++ b/apps/outreach/src/plugins/starlight-plugin-typedoc/starlight.ts @@ -5,7 +5,7 @@ import path from 'node:path'; import type { StarlightPlugin } from '@astrojs/starlight/types'; import { slug } from 'github-slugger'; import { ReferenceReflection, ReflectionKind } from 'typedoc'; -import type { DeclarationReflection, ProjectReflection, ReflectionGroup } from 'typedoc'; +import type { DeclarationReflection, ProjectReflection, ReflectionGroup, RouterTarget } from 'typedoc'; import type { StarlightTypeDocSidebarOptions } from './index'; @@ -25,13 +25,20 @@ export function getSidebarFromReflections( sidebarGroupPlaceholder: SidebarGroup, options: StarlightTypeDocSidebarOptions, reflections: DeclarationReflection | ProjectReflection, + pageUrls: PageUrls, baseOutputDirectory: string ): StarlightUserConfigSidebar { if (!sidebar || sidebar.length === 0) { return sidebar; } - const sidebarGroup = getSidebarGroupFromReflections(options, reflections, baseOutputDirectory, baseOutputDirectory); + const sidebarGroup = getSidebarGroupFromReflections( + options, + reflections, + pageUrls, + baseOutputDirectory, + baseOutputDirectory + ); function replaceSidebarGroupPlaceholder(group: SidebarManualGroup): SidebarGroup { if (group.label === sidebarGroupPlaceholder.label) { @@ -58,18 +65,22 @@ export function getSidebarFromReflections( function getSidebarGroupFromPackageReflections( options: StarlightTypeDocSidebarOptions, reflections: DeclarationReflection | ProjectReflection, + pageUrls: PageUrls, baseOutputDirectory: string ): SidebarGroup { const groups = (reflections.children ?? []).map((child) => { - if (!child.url) { + const childUrl = pageUrls.get(child); + + if (!childUrl) { return undefined; } - const url = path.parse(child.url); + const url = path.parse(childUrl); return getSidebarGroupFromReflections( options, child, + pageUrls, baseOutputDirectory, `${baseOutputDirectory}/${url.dir}`, child.name @@ -86,12 +97,13 @@ function getSidebarGroupFromPackageReflections( function getSidebarGroupFromReflections( options: StarlightTypeDocSidebarOptions, reflections: DeclarationReflection | ProjectReflection, + pageUrls: PageUrls, baseOutputDirectory: string, outputDirectory: string, label?: string ): SidebarGroup { if ((!reflections.groups || reflections.groups.length === 0) && reflections.children) { - return getSidebarGroupFromPackageReflections(options, reflections, outputDirectory); + return getSidebarGroupFromPackageReflections(options, reflections, pageUrls, outputDirectory); } const groups = reflections.groups ?? []; @@ -102,16 +114,19 @@ function getSidebarGroupFromReflections( .flatMap((group) => { if (group.title === 'Modules') { return group.children.map((child) => { - if (!child.url) { + const childUrl = pageUrls.get(child); + + if (!childUrl) { return undefined; } - const url = path.parse(child.url); + const url = path.parse(childUrl); const isParentKindModule = child.parent?.kind === ReflectionKind.Module; return getSidebarGroupFromReflections( { collapsed: true, label: child.name }, child as DeclarationReflection, + pageUrls, baseOutputDirectory, `${outputDirectory}/${isParentKindModule ? url.dir.split('/').slice(1).join('/') : url.dir}` ); @@ -119,14 +134,14 @@ function getSidebarGroupFromReflections( } if (isReferenceReflectionGroup(group)) { - return getReferencesSidebarGroup(group, baseOutputDirectory); + return getReferencesSidebarGroup(group, pageUrls, baseOutputDirectory); } const directory = `${outputDirectory}/${slug(group.title.toLowerCase())}`; // The groups generated using the `@group` tag do not have an associated directory on disk. const isGroupWithDirectory = group.children.some((child) => - path.posix.join(baseOutputDirectory, child.url?.replace('\\', '/') ?? '').startsWith(directory) + path.posix.join(baseOutputDirectory, pageUrls.get(child)?.replace('\\', '/') ?? '').startsWith(directory) ); if (!isGroupWithDirectory) { @@ -149,6 +164,7 @@ function getSidebarGroupFromReflections( function getReferencesSidebarGroup( group: ReflectionGroup, + pageUrls: PageUrls, baseOutputDirectory: string ): SidebarManualGroup | undefined { const referenceItems: LinkItem[] = group.children @@ -164,13 +180,15 @@ function getReferencesSidebarGroup( target = target.parent; } - if (!target.url) { + const targetUrl = pageUrls.get(target); + + if (!targetUrl) { return undefined; } return { label: reference.name, - link: getRelativeURL(target.url, getStarlightTypeDocOutputDirectory(baseOutputDirectory)) + link: getRelativeURL(targetUrl, getStarlightTypeDocOutputDirectory(baseOutputDirectory)) }; }) .filter((item): item is LinkItem => item !== undefined); @@ -260,4 +278,6 @@ type LinkItem = { type AsideType = 'caution' | 'danger' | 'note' | 'tip'; +type PageUrls = ReadonlyMap; + type StarlightUserConfigSidebar = Parameters>[0]['config']['sidebar']; diff --git a/apps/outreach/src/plugins/starlight-plugin-typedoc/theme.ts b/apps/outreach/src/plugins/starlight-plugin-typedoc/theme.ts index 58faa97ad..08515fd7e 100644 --- a/apps/outreach/src/plugins/starlight-plugin-typedoc/theme.ts +++ b/apps/outreach/src/plugins/starlight-plugin-typedoc/theme.ts @@ -1,5 +1,3 @@ -import path from 'node:path'; - import { Reflection } from 'typedoc'; import type { Comment, CommentDisplayPart, Options } from 'typedoc'; import { MarkdownPageEvent, MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown'; @@ -31,13 +29,11 @@ class StarlightTypeDocThemeRenderContext extends MarkdownThemeContext { part.kind === 'inline-tag' && (part.tag === '@link' || part.tag === '@linkcode' || part.tag === '@linkplain') && part.target instanceof Reflection && - typeof part.target.url === 'string' + this.router.hasUrl(part.target) ) { return { ...part, - target: this.getRelativeUrl( - path.posix.join(this.options.getValue('entryPointStrategy') === 'packages' ? '../..' : '..', part.target.url) - ) + target: this.urlTo(part.target) }; } @@ -109,11 +105,11 @@ class StarlightTypeDocThemeRenderContext extends MarkdownThemeContext { this.#markdownThemeContext = new MarkdownThemeContext(theme, event, options); } - override getRelativeUrl(url: string): string { + override urlTo(reflection: Reflection): string { const outputDirectory = this.options.getValue('starlight-typedoc-output'); const baseUrl = typeof outputDirectory === 'string' ? outputDirectory : ''; - return getRelativeURL(url, baseUrl, this.page.url); + return getRelativeURL(this.router.getFullUrl(reflection), baseUrl, this.page.url); } #addAside(markdown: string, ...args: Parameters) { diff --git a/apps/outreach/src/plugins/starlight-plugin-typedoc/typedoc.ts b/apps/outreach/src/plugins/starlight-plugin-typedoc/typedoc.ts index 6d6597bd7..7a541f2cc 100644 --- a/apps/outreach/src/plugins/starlight-plugin-typedoc/typedoc.ts +++ b/apps/outreach/src/plugins/starlight-plugin-typedoc/typedoc.ts @@ -4,11 +4,12 @@ import url from 'node:url'; import type { AstroConfig, AstroIntegrationLogger } from 'astro'; import { Application, PageEvent, ParameterType, RendererEvent } from 'typedoc'; -import type { Reflection, TypeDocOptions } from 'typedoc'; +import type { RouterTarget, TypeDocOptions } from 'typedoc'; import type { MarkdownPageEvent, PluginOptions } from 'typedoc-plugin-markdown'; import { StarlightTypeDocLogger } from './logger'; import { addFrontmatter } from './markdown'; +import { StarlightTypeDocRouter } from './router'; import { getStarlightTypeDocOutputDirectory } from './starlight'; import { StarlightTypeDocTheme } from './theme'; @@ -20,6 +21,7 @@ const defaultTypeDocConfig: TypeDocConfig = { excludeProtected: true, githubPages: false, readme: 'none', + router: 'starlight-typedoc', theme: 'starlight-typedoc' }; @@ -32,8 +34,24 @@ const markdownPluginConfig: TypeDocConfig = { async function generateTypeDoc(options: StarlightTypeDocOptions, config: AstroConfig, logger: AstroIntegrationLogger) { const baseOutputDirectory = options.output; const outputDirectory = `${options.locale}/${baseOutputDirectory}`; + const outputPath = path.join(url.fileURLToPath(config.srcDir), 'content/docs', outputDirectory); + + const app = await bootstrapApp( + options.entryPoints, + options.tsconfig, + outputDirectory, + outputPath, + config.base, + logger + ); + + const pageUrls = new Map(); + app.renderer.on(RendererEvent.END, ({ pages }) => { + for (const page of pages) { + pageUrls.set(page.model, page.url); + } + }); - const app = await bootstrapApp(options.entryPoints, options.tsconfig, outputDirectory, config.base, logger); const reflections = await app.convert(); if ( @@ -43,17 +61,16 @@ async function generateTypeDoc(options: StarlightTypeDocOptions, config: AstroCo throw new Error('Failed to generate TypeDoc documentation.'); } - const outputPath = path.join(url.fileURLToPath(config.srcDir), 'content/docs', outputDirectory); - - await app.generateDocs(reflections, outputPath); + await app.generateOutputs(reflections); - return { baseOutputDirectory, reflections }; + return { baseOutputDirectory, pageUrls, reflections }; } async function bootstrapApp( entryPoints: TypeDocOptions['entryPoints'], tsconfig: TypeDocOptions['tsconfig'], outputDirectory: string, + outputPath: string, base: string, logger: AstroIntegrationLogger ) { @@ -63,17 +80,18 @@ async function bootstrapApp( ...defaultTypeDocConfig, ...markdownPluginConfig, entryPoints, + outputs: [{ name: 'markdown', path: outputPath }], plugin: ['typedoc-plugin-markdown'], tsconfig }); app.logger = new StarlightTypeDocLogger(logger); - // @ts-expect-error - inherited code + app.renderer.defineRouter('starlight-typedoc', StarlightTypeDocRouter); app.renderer.defineTheme('starlight-typedoc', StarlightTypeDocTheme); - app.renderer.on(PageEvent.BEGIN, (event: PageEvent) => { + app.renderer.on(PageEvent.BEGIN, (event) => { // @ts-expect-error - inherited code onRendererPageBegin(event); }); - app.renderer.on(PageEvent.END, (event: PageEvent) => { + app.renderer.on(PageEvent.END, (event) => { // @ts-expect-error - inherited code const shouldRemovePage = onRendererPageEnd(event); if (shouldRemovePage) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb093daac..4ca844280 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -530,11 +530,11 @@ importers: specifier: 'catalog:' version: 4.3.3 typedoc: - specifier: 0.27.x - version: 0.27.9(typescript@6.0.3) + specifier: ^0.28.20 + version: 0.28.20(typescript@6.0.3) typedoc-plugin-markdown: - specifier: 4.4.x - version: 4.4.2(typedoc@0.27.9(typescript@6.0.3)) + specifier: ^4.13.1 + version: 4.13.1(typedoc@0.28.20(typescript@6.0.3)) apps/playground: dependencies: @@ -2892,8 +2892,8 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - '@gerrit0/mini-shiki@1.27.2': - resolution: {integrity: sha512-GeWyHz8ao2gBiUW4OJnQDxXQnFgZQwwQk05t/CVVgNBN7/rK8XZ7xY6YhLVv9tH3VppWWmr9DCl3MwemB/i+Og==} + '@gerrit0/mini-shiki@3.23.0': + resolution: {integrity: sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==} '@heroicons/react@2.2.0': resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} @@ -5110,9 +5110,6 @@ packages: '@shikijs/engine-javascript@3.23.0': resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} - '@shikijs/engine-oniguruma@1.29.2': - resolution: {integrity: sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==} - '@shikijs/engine-oniguruma@3.23.0': resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} @@ -5122,9 +5119,6 @@ packages: '@shikijs/themes@3.23.0': resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} - '@shikijs/types@1.29.2': - resolution: {integrity: sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==} - '@shikijs/types@3.23.0': resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} @@ -6377,9 +6371,6 @@ packages: brace-expansion@1.1.15: resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} - brace-expansion@2.1.1: - resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} - brace-expansion@5.0.6: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} @@ -8278,8 +8269,8 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - linkify-it@5.0.1: - resolution: {integrity: sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==} + linkify-it@5.0.2: + resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} load-esm@1.0.3: resolution: {integrity: sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==} @@ -8380,8 +8371,8 @@ packages: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} - markdown-it@14.2.0: - resolution: {integrity: sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==} + markdown-it@14.3.2: + resolution: {integrity: sha512-sHHjZ5fJKlgrG4qns2YwVcdNep35h5fERrfkD2YNsb9UFk0UIHarbiTaHKVMlPuWAoiilyK8Fv/jAm11slsY7Q==} hasBin: true markdown-table@3.0.4: @@ -8626,10 +8617,6 @@ packages: minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} - engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -10297,15 +10284,15 @@ packages: resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} engines: {node: '>= 0.4'} - typedoc-plugin-markdown@4.4.2: - resolution: {integrity: sha512-kJVkU2Wd+AXQpyL6DlYXXRrfNrHrEIUgiABWH8Z+2Lz5Sq6an4dQ/hfvP75bbokjNDUskOdFlEEm/0fSVyC7eg==} + typedoc-plugin-markdown@4.13.1: + resolution: {integrity: sha512-d3jchJ4CyIrV9w9uozTSkfMYG8IEM1RvvGOaybJ/3rRporzoX7p0lprffo95GOGUviQqP0phjHJE3bY8PQbilA==} engines: {node: '>= 18'} peerDependencies: - typedoc: 0.27.x + typedoc: 0.28.x - typedoc@0.27.9: - resolution: {integrity: sha512-/z585740YHURLl9DN2jCWe6OW7zKYm6VoQ93H0sxZ1cwHQEQrUn5BJrEnkWhfzUdyO+BLGjnKUZ9iz9hKloFDw==} - engines: {node: '>= 18'} + typedoc@0.28.20: + resolution: {integrity: sha512-uSKqkh8Cr48vllnEy+jdaAgOeR6Y+QCBW7usgUsKj7gJEfR7stw9U/fE49LBnj2tPRKPY0c0EBJSWe9Appmplg==} + engines: {node: '>= 18', pnpm: '>= 10'} hasBin: true peerDependencies: typescript: 6.0.x @@ -12392,10 +12379,12 @@ snapshots: '@floating-ui/utils@0.2.11': {} - '@gerrit0/mini-shiki@1.27.2': + '@gerrit0/mini-shiki@3.23.0': dependencies: - '@shikijs/engine-oniguruma': 1.29.2 - '@shikijs/types': 1.29.2 + '@shikijs/engine-oniguruma': 3.23.0 + '@shikijs/langs': 3.23.0 + '@shikijs/themes': 3.23.0 + '@shikijs/types': 3.23.0 '@shikijs/vscode-textmate': 10.0.2 '@heroicons/react@2.2.0(react@vendor+react@19.x)': @@ -14549,11 +14538,6 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 oniguruma-to-es: 4.3.6 - '@shikijs/engine-oniguruma@1.29.2': - dependencies: - '@shikijs/types': 1.29.2 - '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/engine-oniguruma@3.23.0': dependencies: '@shikijs/types': 3.23.0 @@ -14567,11 +14551,6 @@ snapshots: dependencies: '@shikijs/types': 3.23.0 - '@shikijs/types@1.29.2': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - '@shikijs/types@3.23.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 @@ -16103,10 +16082,6 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.1: - dependencies: - balanced-match: 1.0.2 - brace-expansion@5.0.6: dependencies: balanced-match: 4.0.4 @@ -18323,7 +18298,7 @@ snapshots: lines-and-columns@1.2.4: {} - linkify-it@5.0.1: + linkify-it@5.0.2: dependencies: uc.micro: 2.1.0 @@ -18413,11 +18388,11 @@ snapshots: markdown-extensions@2.0.0: {} - markdown-it@14.2.0: + markdown-it@14.3.2: dependencies: argparse: 2.0.1 entities: 4.5.0 - linkify-it: 5.0.1 + linkify-it: 5.0.2 mdurl: 2.0.0 punycode.js: 2.3.1 uc.micro: 2.1.0 @@ -18934,10 +18909,6 @@ snapshots: dependencies: brace-expansion: 1.1.15 - minimatch@9.0.9: - dependencies: - brace-expansion: 2.1.1 - minimist@1.2.8: {} minipass@7.1.3: {} @@ -20908,16 +20879,16 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typedoc-plugin-markdown@4.4.2(typedoc@0.27.9(typescript@6.0.3)): + typedoc-plugin-markdown@4.13.1(typedoc@0.28.20(typescript@6.0.3)): dependencies: - typedoc: 0.27.9(typescript@6.0.3) + typedoc: 0.28.20(typescript@6.0.3) - typedoc@0.27.9(typescript@6.0.3): + typedoc@0.28.20(typescript@6.0.3): dependencies: - '@gerrit0/mini-shiki': 1.27.2 + '@gerrit0/mini-shiki': 3.23.0 lunr: 2.3.9 - markdown-it: 14.2.0 - minimatch: 9.0.9 + markdown-it: 14.3.2 + minimatch: 10.2.5 typescript: 6.0.3 yaml: 2.9.0 From f5650f59d86cfa233d036923f0f5a312da5be318 Mon Sep 17 00:00:00 2001 From: joshunrau Date: Wed, 7 Oct 2026 11:34:17 -0400 Subject: [PATCH 12/14] build: upgrade casl to 7 @casl/ability moves to 7.0.1 and @casl/prisma to 2.0.2, the newest pair past the 7-day release age (#1611). @casl/prisma 2 peers @casl/ability ^7, so the two move together. apps/api now takes @casl/ability from the catalog like web and schemas do, and the lockfile holds one copy of each. PureAbility is renamed Ability in 7. The api's AppAbility and Permission types, the web's ability type and the schemas' BaseAppAbility follow the rename, as does the instanceof check in JwtAuthGuard. createPrismaAbility and createMongoAbility both construct that same Ability class, and the production bundle carries a single copy of it, so the guard still recognises every ability the api builds. createAppAbility now hands AppAbility itself to createPrismaAbility's new generic, which it satisfies without a cast. createAccessibleByFactory is gone from @casl/prisma/runtime, so accessibleQuery is rewritten on accessibleBy(ability, action).ofType(). That call no longer throws ForbiddenError when no rule grants the action; it returns { OR: [] } instead. Prisma ignores an empty OR nested in an AND (prisma/prisma#17367, reproduced here against MongoDB), and every service passes accessibleQuery inside an AND, so forwarding the marker would hand an ability with no rule every group's rows. As the maintainer decided, accessibleQuery now throws ForbiddenError itself, with the message and fields @casl/prisma 1 set. ofType returns { OR: [] } exactly when @casl/prisma 1 threw: rulesToCondition in 7 and rulesToQuery in 6 return null under the same condition, cannot rules included, which a comparison of the two over 400,000 random rule sets confirmed. ability.can is not that condition in 7, because it now reads a cannot rule whose conditions match everything as unconditional, but it refuses whenever the query would throw, so the ability.can guards in InstrumentsService and SubjectsService still pre-empt every throw. createCaslExtension is not added to the Prisma client. Its only job is to rewrite that marker into an empty result, and no marker can reach Prisma any more. accessibleBy is called only inside accessibleQuery, which throws on it. The only other source of an empty OR would be a rule's own conditions, and no rule the ability factory writes contains an OR, nor does any where clause the api builds itself. The ability.utils tests now run against the real CASL instead of a mocked factory. They pin the no-rule throw, the cannot cases, and the one case where ability.can and the throw disagree. The ability factory tests pin that a group manager's in and hasSome group conditions reach the where as filters, over an empty group list too, rather than being read as conditions that match everything. auth-and-permissions.md now describes the throw as accessibleQuery's own and records why the extension is absent. Co-Authored-By: Claude Opus 5.5 --- .../docs/architecture/auth-and-permissions.md | 17 ++++- apps/api/package.json | 4 +- .../auth/__tests__/ability.factory.test.ts | 23 ++++++ .../src/auth/__tests__/ability.utils.test.ts | 61 +++++++++++---- apps/api/src/auth/ability.utils.ts | 24 +++--- apps/api/src/auth/auth.types.ts | 9 +-- apps/api/src/auth/guards/jwt-auth.guard.ts | 4 +- .../instrument-records.service.ts | 2 +- apps/web/src/store/slices/auth.slice.ts | 5 +- apps/web/src/testing/stubs.ts | 5 +- packages/schemas/src/core/core.ts | 4 +- pnpm-lock.yaml | 74 +++++++++---------- pnpm-workspace.yaml | 2 +- 13 files changed, 153 insertions(+), 81 deletions(-) diff --git a/.agents/docs/architecture/auth-and-permissions.md b/.agents/docs/architecture/auth-and-permissions.md index fc5a017fe..36ac2eca1 100644 --- a/.agents/docs/architecture/auth-and-permissions.md +++ b/.agents/docs/architecture/auth-and-permissions.md @@ -72,14 +72,23 @@ that the controller actually forwards `@CurrentUser('ability')` — `EntityOpera is optional, so a controller that simply never passes it compiles and runs. **A defined ability holding no rule for the queried subject does not produce a deny-all filter — -CASL's `accessibleBy` throws `ForbiddenError`.** That error is not an `HttpException`, so the +`accessibleQuery` throws CASL's `ForbiddenError`.** That error is not an `HttpException`, so the global exception filter renders it as a 500. It is reachable whenever the route guard names a different subject than the service queries: a STANDARD user passes a `read Instrument` guard but holds no rule on `InstrumentRecord`, so a record query made on their behalf throws. The throw is deliberate — `accessibleQuery` is kept as-is rather than returning a deny-all filter — so where such a caller is legitimate, guard the query with `ability.can(action, subject)` and return the empty result instead; `findInstrumentIdsBySubject` in `src/instruments/instruments.service.ts` is -the reference example. +the reference example. `ability.can` refuses in every case where the query would throw, so the +guard always pre-empts it. + +The throw is `accessibleQuery`'s own. `@casl/prisma` 2's `accessibleBy(...).ofType(...)` no longer +throws: it returns `{ OR: [] }`, which Prisma ignores once it is nested in an `AND` +(prisma/prisma#17367, reproducible on this MongoDB stack), so passed on it would lift the +restriction rather than apply it. `accessibleQuery` throws on that marker instead, under exactly the +condition `@casl/prisma` 1 threw. That is also why the client in `src/core/prisma.ts` carries no +`createCaslExtension()`, which upstream offers to turn the marker into an empty result: the marker +never reaches Prisma. Call `accessibleBy` only through `accessibleQuery`. One call site uses `{ ...accessibleQuery(...), id: subjectId }` instead of `AND: [...]` (`src/subjects/subjects.service.ts`). Spreading merges keys and will silently lose a condition if @@ -208,8 +217,8 @@ add it to the `$GroupScopableSubjectName` exclusion. - `src/auth/__tests__/ability.factory.test.ts` — role-to-rule mapping. Each case asserts an allow _and_ a deny. -- `src/auth/__tests__/ability.utils.test.ts` — pins the `undefined` ability behavior and - `forcedAppSubject`. +- `src/auth/__tests__/ability.utils.test.ts` — pins the `undefined` ability behavior, the + no-rule throw (including `cannot` rules) and `forcedAppSubject`, against the real CASL. - `src/auth/guards/__tests__/jwt-auth.guard.test.ts` — the guard's own branches. Service tests mock the Prisma layer, so they verify the arguments passed to the model, not the diff --git a/apps/api/package.json b/apps/api/package.json index 2b15688ef..23f3c09b8 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -17,8 +17,8 @@ "dependencies": { "@aws-sdk/client-s3": "^3.1141.0", "@aws-sdk/s3-request-presigner": "^3.1141.0", - "@casl/ability": "^6.7.5", - "@casl/prisma": "^1.5.1", + "@casl/ability": "catalog:", + "@casl/prisma": "^2.0.2", "@douglasneuroinformatics/libcrypto": "catalog:", "@douglasneuroinformatics/libjs": "catalog:", "@douglasneuroinformatics/libnest": "^10.0.0", diff --git a/apps/api/src/auth/__tests__/ability.factory.test.ts b/apps/api/src/auth/__tests__/ability.factory.test.ts index 52c025159..04bc7d9c6 100644 --- a/apps/api/src/auth/__tests__/ability.factory.test.ts +++ b/apps/api/src/auth/__tests__/ability.factory.test.ts @@ -89,6 +89,29 @@ describe('AbilityFactory', () => { expect(ability.can('manage', subject('Group', { id: 'group-2' }) as any)).toBe(false); }); + // CASL 7 treats conditions that semantically match everything as no conditions at all, which in a + // `where` means no restriction. These pin that a group condition, even over no groups, is not one. + it.each([ + ['InstrumentRecord', ['group-1', 'group-2'], { groupId: { in: ['group-1', 'group-2'] } }], + ['Session', ['group-1', 'group-2'], { groupId: { in: ['group-1', 'group-2'] } }], + ['Subject', ['group-1', 'group-2'], { groupIds: { hasSome: ['group-1', 'group-2'] } }], + ['User', ['group-1', 'group-2'], { groupIds: { hasSome: ['group-1', 'group-2'] } }], + ['InstrumentRecord', [], { groupId: { in: [] } }], + ['Subject', [], { groupIds: { hasSome: [] } }] + ] as const)("should confine a group manager's %s query to the groups %j", (modelName, groupIds, condition) => { + const ability = abilityFactory.createForPayload({ + additionalPermissions: undefined, + basePermissionLevel: 'GROUP_MANAGER', + firstName: 'Test', + groups: groupIds.map((id) => ({ id })), + id: 'user-1', + lastName: 'User', + username: 'manager-user' + } as any); + + expect(accessibleQuery(ability, 'read', modelName)).toStrictEqual({ OR: [condition] }); + }); + it('should restrict series deletion to the manager group', () => { const ability = abilityFactory.createForPayload({ additionalPermissions: undefined, diff --git a/apps/api/src/auth/__tests__/ability.utils.test.ts b/apps/api/src/auth/__tests__/ability.utils.test.ts index b016e9ba1..1c18ae07c 100644 --- a/apps/api/src/auth/__tests__/ability.utils.test.ts +++ b/apps/api/src/auth/__tests__/ability.utils.test.ts @@ -1,25 +1,58 @@ -import { describe, expect, it, vi } from 'vitest'; +import { ForbiddenError } from '@casl/ability'; +import { describe, expect, it } from 'vitest'; import { accessibleQuery, createAppAbility, forcedAppSubject } from '../ability.utils'; -const accessibleBy = vi.hoisted(() => vi.fn()); - -vi.mock('@casl/prisma/runtime', () => ({ - createAccessibleByFactory: () => accessibleBy -})); - describe('accessibleQuery', () => { it('should return an empty object if ability is undefined', () => { expect(accessibleQuery(undefined, 'manage', 'User')).toStrictEqual({}); - expect(accessibleBy).not.toHaveBeenCalled(); }); - it('should call accessibleBy with the correct parameters and return the result of accessibleBy for the model', () => { - accessibleBy.mockReturnValueOnce({ - User: 'QUERY' + + it('should return an empty object for an unconditional rule, since it restricts nothing', () => { + const ability = createAppAbility([{ action: 'manage', subject: 'all' }]); + expect(accessibleQuery(ability, 'read', 'Subject')).toStrictEqual({}); + }); + + it('should return the conditions of the rules granting the action on the model', () => { + const ability = createAppAbility([ + { action: 'read', conditions: { groupIds: { hasSome: ['group-1'] } }, subject: 'Subject' }, + { action: 'read', conditions: { groupId: { in: ['group-1'] } }, subject: 'Session' } + ]); + expect(accessibleQuery(ability, 'read', 'Subject')).toStrictEqual({ OR: [{ groupIds: { hasSome: ['group-1'] } }] }); + }); + + it('should throw a ForbiddenError when no rule grants the action on the model, rather than filter', () => { + const ability = createAppAbility([{ action: 'read', subject: 'Session' }]); + expect(() => accessibleQuery(ability, 'read', 'Subject')).toThrow(ForbiddenError); + expect(() => accessibleQuery(ability, 'read', 'Subject')).toThrow(`It's not allowed to run "read" on "Subject"`); + }); + + it('should throw when an unconditional cannot rule outranks every can rule', () => { + const ability = createAppAbility([ + { action: 'read', conditions: { groupIds: { hasSome: ['group-1'] } }, subject: 'Subject' }, + { action: 'read', inverted: true, subject: 'Subject' } + ]); + expect(() => accessibleQuery(ability, 'read', 'Subject')).toThrow(ForbiddenError); + }); + + it('should throw when the only rules for the model are conditional cannot rules', () => { + const ability = createAppAbility([ + { action: 'read', conditions: { groupIds: { hasSome: ['group-1'] } }, inverted: true, subject: 'Subject' } + ]); + expect(() => accessibleQuery(ability, 'read', 'Subject')).toThrow(ForbiddenError); + }); + + // `ability.can` refuses here, since CASL 7 reads a cannot rule matching everything as unconditional, + // but the query is a filter that matches nothing, which is what @casl/prisma 1 returned too. + it('should filter rather than throw when a cannot rule matching everything outranks a can rule', () => { + const ability = createAppAbility([ + { action: 'read', conditions: { groupIds: { hasSome: ['group-1'] } }, subject: 'Subject' }, + { action: 'read', conditions: {}, inverted: true, subject: 'Subject' } + ]); + expect(ability.can('read', 'Subject')).toBe(false); + expect(accessibleQuery(ability, 'read', 'Subject')).toStrictEqual({ + OR: [{ AND: [{ groupIds: { hasSome: ['group-1'] } }, { NOT: {} }] }] }); - const ability = vi.fn(); - expect(accessibleQuery(ability as any, 'manage', 'User')).toStrictEqual('QUERY'); - expect(accessibleBy).toHaveBeenCalledExactlyOnceWith(ability, 'manage'); }); }); diff --git a/apps/api/src/auth/ability.utils.ts b/apps/api/src/auth/ability.utils.ts index 1e7888251..e10306e69 100644 --- a/apps/api/src/auth/ability.utils.ts +++ b/apps/api/src/auth/ability.utils.ts @@ -1,14 +1,10 @@ -import { detectSubjectType, subject } from '@casl/ability'; -import { createPrismaAbility } from '@casl/prisma'; -import type { PrismaQuery } from '@casl/prisma'; -import { createAccessibleByFactory } from '@casl/prisma/runtime'; +import { detectSubjectType, ForbiddenError, subject } from '@casl/ability'; +import { accessibleBy, createPrismaAbility } from '@casl/prisma'; import type { AppSubject, Prisma } from '@prisma/client'; import type { PrismaModelWhereInputMap } from '@/core/prisma'; -import type { AppAbilities, AppAbility, AppAction, AppSubjectModels, AppSubjectName, Permission } from './auth.types'; - -const accessibleBy = createAccessibleByFactory(); +import type { AppAbility, AppAction, AppSubjectModels, AppSubjectName, Permission } from './auth.types'; export function detectAppSubject(obj: { [key: string]: any }) { if (typeof obj.__modelName === 'string') { @@ -28,7 +24,7 @@ export function forcedAppSubject(permissions, { + return createPrismaAbility(permissions, { detectSubjectType: detectAppSubject }); } @@ -41,5 +37,15 @@ export function accessibleQuery( if (!ability) { return {}; } - return accessibleBy(ability, action)[modelName]!; + const query = accessibleBy(ability, action).ofType(modelName); + // `ofType` returns `{ OR: [] }` exactly when no rule grants the action on the model, which is when + // @casl/prisma 1 threw this error. Prisma ignores an empty `OR` nested in an `AND` + // (prisma/prisma#17367), so passing it on would lift the restriction instead of applying it. + if (query.OR?.length === 0) { + const error = ForbiddenError.from(ability).setMessage(`It's not allowed to run "${action}" on "${modelName}"`); + error.action = action; + error.subject = error.subjectType = modelName; + throw error; + } + return query; } diff --git a/apps/api/src/auth/auth.types.ts b/apps/api/src/auth/auth.types.ts index 2663f77b5..36c5d25b2 100644 --- a/apps/api/src/auth/auth.types.ts +++ b/apps/api/src/auth/auth.types.ts @@ -1,5 +1,4 @@ -import { PureAbility } from '@casl/ability'; -import type { RawRuleOf } from '@casl/ability'; +import type { Ability, RawRuleOf } from '@casl/ability'; import type { PrismaQuery, Subjects } from '@casl/prisma'; import { Prisma } from '@prisma/client'; import type { DefaultSelection } from '@prisma/client/runtime/library'; @@ -16,8 +15,8 @@ type AppSubjectName = Extract; type AppAbilities = [AppAction, AppSubjects]; -type AppAbility = PureAbility; +type AppAbility = Ability; -type Permission = RawRuleOf>; +type Permission = RawRuleOf>; -export type { AppAbilities, AppAbility, AppAction, AppSubjectModels, AppSubjectName, Permission }; +export type { AppAbility, AppAction, AppSubjectModels, AppSubjectName, Permission }; diff --git a/apps/api/src/auth/guards/jwt-auth.guard.ts b/apps/api/src/auth/guards/jwt-auth.guard.ts index 486f8f06b..37a02e78c 100644 --- a/apps/api/src/auth/guards/jwt-auth.guard.ts +++ b/apps/api/src/auth/guards/jwt-auth.guard.ts @@ -1,4 +1,4 @@ -import { PureAbility } from '@casl/ability'; +import { Ability } from '@casl/ability'; import { LoggingService } from '@douglasneuroinformatics/libnest'; import { Injectable, InternalServerErrorException } from '@nestjs/common'; import type { ExecutionContext } from '@nestjs/common'; @@ -55,7 +55,7 @@ export class JwtAuthGuard extends AuthGuard('jwt') { const ability = request.user?.ability; - if (!(ability instanceof PureAbility)) { + if (!(ability instanceof Ability)) { this.loggingService.error('User property of request does not include expected AppAbility'); throw new InternalServerErrorException(); } diff --git a/apps/api/src/instrument-records/instrument-records.service.ts b/apps/api/src/instrument-records/instrument-records.service.ts index 18355776c..6fa7e3788 100644 --- a/apps/api/src/instrument-records/instrument-records.service.ts +++ b/apps/api/src/instrument-records/instrument-records.service.ts @@ -480,7 +480,7 @@ export class InstrumentRecordsService { // Only the rows this call wrote. Filtering by instrument alone returned every group's records // when `groupId` was omitted, and `accessibleQuery` cannot scope this read: a STANDARD uploader - // holds no `read InstrumentRecord` rule, which makes CASL throw rather than filter. + // holds no `read InstrumentRecord` rule, which makes `accessibleQuery` throw rather than filter. return this.instrumentRecordModel.findMany({ where: { sessionId: { in: sessions.map((session) => session.id) } diff --git a/apps/web/src/store/slices/auth.slice.ts b/apps/web/src/store/slices/auth.slice.ts index f71119f2a..f40c21e8d 100644 --- a/apps/web/src/store/slices/auth.slice.ts +++ b/apps/web/src/store/slices/auth.slice.ts @@ -1,4 +1,5 @@ -import { createMongoAbility, PureAbility } from '@casl/ability'; +import { createMongoAbility } from '@casl/ability'; +import type { Ability } from '@casl/ability'; import type { TokenPayload } from '@opendatacapture/schemas/auth'; import type { AppAction, AppSubjectName } from '@opendatacapture/schemas/core'; import { jwtDecode } from 'jwt-decode'; @@ -7,7 +8,7 @@ import type { AuthSlice, SliceCreator } from '../types'; const parseAccessToken = (accessToken: string) => { const { groups, permissions, ...rest } = jwtDecode(accessToken); - const ability = createMongoAbility>(permissions); + const ability = createMongoAbility>(permissions); return { currentGroup: groups[0], currentUser: { diff --git a/apps/web/src/testing/stubs.ts b/apps/web/src/testing/stubs.ts index f118a39de..fa9e36a47 100644 --- a/apps/web/src/testing/stubs.ts +++ b/apps/web/src/testing/stubs.ts @@ -1,11 +1,12 @@ -import { createMongoAbility, PureAbility } from '@casl/ability'; +import { createMongoAbility } from '@casl/ability'; +import type { Ability } from '@casl/ability'; import type { AppAction, AppSubjectName } from '@opendatacapture/schemas/core'; import type { Session } from '@opendatacapture/schemas/session'; import type { CurrentUser } from '@/store/types'; export const currentUser: CurrentUser = Object.freeze({ - ability: createMongoAbility>([{ action: 'manage', subject: 'all' }]), + ability: createMongoAbility>([{ action: 'manage', subject: 'all' }]), firstName: 'Jane', groups: [], id: '123', diff --git a/packages/schemas/src/core/core.ts b/packages/schemas/src/core/core.ts index dec806e8a..c2831a50b 100644 --- a/packages/schemas/src/core/core.ts +++ b/packages/schemas/src/core/core.ts @@ -1,4 +1,4 @@ -import type { PureAbility, RawRuleOf } from '@casl/ability'; +import type { Ability, RawRuleOf } from '@casl/ability'; import { licenses } from '@opendatacapture/licenses'; import type { LicenseIdentifier } from '@opendatacapture/licenses'; import type { Language as InstrumentAuthoringLanguage, Json, JsonLiteral } from '@opendatacapture/runtime-core'; @@ -38,7 +38,7 @@ export const $GroupScopableSubjectName = $AppSubjectName.exclude(['all', 'Instru export const isGroupScopableSubject = (subject: AppSubjectName): subject is GroupScopableSubjectName => $GroupScopableSubjectName.safeParse(subject).success; -export type BaseAppAbility = PureAbility<[AppAction, AppSubjectName]>; +export type BaseAppAbility = Ability<[AppAction, AppSubjectName]>; export type UserPermission = z.infer; export const $UserPermission = z diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ca844280..cce5f98a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,8 +7,8 @@ settings: catalogs: default: '@casl/ability': - specifier: ^6.7.1 - version: 6.8.1 + specifier: ^7.0.1 + version: 7.0.1 '@commitlint/cli': specifier: ^21.2.3 version: 21.2.3 @@ -215,11 +215,11 @@ importers: specifier: ^3.1141.0 version: 3.1141.0 '@casl/ability': - specifier: ^6.7.5 - version: 6.8.1 + specifier: 'catalog:' + version: 7.0.1 '@casl/prisma': - specifier: ^1.5.1 - version: 1.6.2(@casl/ability@6.8.1)(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3)) + specifier: ^2.0.2 + version: 2.0.2(@casl/ability@7.0.1)(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3)) '@douglasneuroinformatics/libcrypto': specifier: 'catalog:' version: 0.0.6 @@ -655,7 +655,7 @@ importers: dependencies: '@casl/ability': specifier: 'catalog:' - version: 6.8.1 + version: 7.0.1 '@douglasneuroinformatics/libjs': specifier: 'catalog:' version: 3.2.1(neverthrow@8.2.0)(zod@vendor+zod@3.x) @@ -1121,7 +1121,7 @@ importers: dependencies: '@casl/ability': specifier: 'catalog:' - version: 6.8.1 + version: 7.0.1 '@douglasneuroinformatics/libjs': specifier: 'catalog:' version: 3.2.1(neverthrow@8.2.0)(zod@vendor+zod@3.x) @@ -2015,14 +2015,14 @@ packages: resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==} engines: {node: '>=18'} - '@casl/ability@6.8.1': - resolution: {integrity: sha512-VX5DD1JbSP/DdewZnNwXXaCzve+0pLe14mcUj2l93CdOFAQUT/ylAptNqxf3Wc/jlsuSanAgXza4Z1Iq23dzpQ==} + '@casl/ability@7.0.1': + resolution: {integrity: sha512-krOW1zQGEie5lH6fpryrcsPvc5r2pe5Vn5xbthuzc5X9gwzHIyZgGGgw9dMoSLHXdjB4BIVxGG/HBkG+1NuyZQ==} - '@casl/prisma@1.6.2': - resolution: {integrity: sha512-w0Y8pB/Zp5bSl5E3Ffn0tLazZ2GQZkL64lf+YDevoaDB1+K6RUOFCGVkvE3bYYv+qLYTgO261k99GufIJ4iHeg==} + '@casl/prisma@2.0.2': + resolution: {integrity: sha512-IYp9rABl8s9fF+4NubSahYMXqwLbmVCz8Hd9Xa4MbS6xMkSRTTa+tq9oKQFd/SJ6VoLe2oY+7Wg0eF9CjOCnYA==} peerDependencies: - '@casl/ability': ^5.3.0 || ^6.0.0 - '@prisma/client': ^2.14.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@casl/ability': ^7.0.0 + '@prisma/client': ^4.16.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 '@commander-js/extra-typings@13.1.0': resolution: {integrity: sha512-q5P52BYb1hwVWE6dtID7VvuJWrlfbCv4klj7BjUUOqMz4jbSZD4C9fJ9lRjL2jnBGTg+gDDlaXN51rkWcLk4fg==} @@ -5936,17 +5936,17 @@ packages: resolution: {integrity: sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@ucast/core@1.10.2': - resolution: {integrity: sha512-ons5CwXZ/51wrUPfoduC+cO7AS1/wRb0ybpQJ9RrssossDxVy4t49QxWoWgfBDvVKsz9VXzBk9z0wqTdZ+Cq8g==} + '@ucast/core@2.0.0': + resolution: {integrity: sha512-4XVx6LzPXZGvnZO5jp39cm/G4UvuwvEdtmg+9+4+zl6uFkCcB7UJacvtMYeBE56GJVT99Zqy6Pii7dGJq3Kz9Q==} - '@ucast/js@3.1.0': - resolution: {integrity: sha512-eJ7yQeYtMK85UZjxoxBEbTWx6UMxEXKbjVyp+NlzrT5oMKV5Gpo/9bjTl3r7msaXTVC8iD9NJacqJ8yp7joX+Q==} + '@ucast/js@4.0.1': + resolution: {integrity: sha512-9O5xPBvwEWQk2WvO69Eh2WJB8QljVZ2vRVdFvfnKjlZwWXcYxp1lqLBhwXBU1AtuSgCvKhJPkXdjKJggUmAmQQ==} - '@ucast/mongo2js@1.4.1': - resolution: {integrity: sha512-9aeg5cmqwRQnKCXHN6I17wk83Rcm487bHelaG8T4vfpWneAI469wSI3Srnbu+PuZ5znWRbnwtVq9RgPL+bN6CA==} + '@ucast/mongo2js@2.0.0': + resolution: {integrity: sha512-vNBZzRnsfLr/TSxEoxz6W6hHQ5tmWsfEeC0nCq5z8RezC1AqIRy3cfHm8AGvlGtcn+cTSFQcZremfqnz6wm+nQ==} - '@ucast/mongo@2.4.3': - resolution: {integrity: sha512-XcI8LclrHWP83H+7H2anGCEeDq0n+12FU2mXCTz6/Tva9/9ddK/iacvvhCyW6cijAAOILmt0tWplRyRhVyZLsA==} + '@ucast/mongo@3.0.0': + resolution: {integrity: sha512-kwuSH+kdB4GCR0LGhy/PEDm4PCflur89AlK82kNiYD0FvsA8A/p+0sx7m+/R8mMFAlmlkAd3VXp7sM/cLLYWYg==} '@ungap/structured-clone@1.3.1': resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} @@ -11514,16 +11514,16 @@ snapshots: dependencies: fontkitten: 1.0.3 - '@casl/ability@6.8.1': + '@casl/ability@7.0.1': dependencies: - '@ucast/mongo2js': 1.4.1 + '@ucast/mongo2js': 2.0.0 - '@casl/prisma@1.6.2(@casl/ability@6.8.1)(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3))': + '@casl/prisma@2.0.2(@casl/ability@7.0.1)(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3))': dependencies: - '@casl/ability': 6.8.1 + '@casl/ability': 7.0.1 '@prisma/client': 6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3) - '@ucast/core': 1.10.2 - '@ucast/js': 3.1.0 + '@ucast/core': 2.0.0 + '@ucast/js': 4.0.1 '@commander-js/extra-typings@13.1.0(commander@13.1.0)': dependencies: @@ -15494,21 +15494,21 @@ snapshots: '@typescript-eslint/types': 8.60.1 eslint-visitor-keys: 5.0.1 - '@ucast/core@1.10.2': {} + '@ucast/core@2.0.0': {} - '@ucast/js@3.1.0': + '@ucast/js@4.0.1': dependencies: - '@ucast/core': 1.10.2 + '@ucast/core': 2.0.0 - '@ucast/mongo2js@1.4.1': + '@ucast/mongo2js@2.0.0': dependencies: - '@ucast/core': 1.10.2 - '@ucast/js': 3.1.0 - '@ucast/mongo': 2.4.3 + '@ucast/core': 2.0.0 + '@ucast/js': 4.0.1 + '@ucast/mongo': 3.0.0 - '@ucast/mongo@2.4.3': + '@ucast/mongo@3.0.0': dependencies: - '@ucast/core': 1.10.2 + '@ucast/core': 2.0.0 '@ungap/structured-clone@1.3.1': {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 9582b56eb..08171da0c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -13,7 +13,7 @@ allowBuilds: sharp: true blockExoticSubdeps: true catalog: - '@casl/ability': '^6.7.1' + '@casl/ability': '^7.0.1' '@commitlint/cli': '^21.2.3' '@commitlint/config-conventional': '^21.2.3' '@douglasneuroinformatics/esbuild-plugin-prisma': 'latest' From 1dbe5c751c5f5c937084921fb6fdcdba77b78d98 Mon Sep 17 00:00:00 2001 From: joshunrau Date: Wed, 7 Oct 2026 12:16:42 -0400 Subject: [PATCH 13/14] build(playground): upgrade monaco-editor to 0.57 monaco-editor moves from 0.54.0 to ^0.57.0, the newest release past the 7-day release age (#1615), and the lockfile holds one copy of it. @monaco-editor/react is removed and replaced by a small MonacoEditor component in the Editor folder. Its 4.7.0 types import monaco-editor/esm/vs/editor/editor.api, which the exports map added in monaco-editor 0.56 no longer resolves, and 4.8 has been a release candidate for 11 months. The component keeps exactly what EditorPane relied on. Each path gets one model, reused if it exists and otherwise created from the default value in the default language, and a path change swaps the editor to that model. Models are never disposed, so a file keeps its content and undo history across EditorPane's remount on every file switch, and each model's cursor and scroll position are restored when its file is shown again, as the library's default view state saving did. Options apply at creation and update when they change, the theme is set and follows the app's toggle, onChange receives the content on every edit, onMount runs once after creation, and unmounting disposes the editor and nothing else. setup.ts no longer needs the library's loader, so its top-level await is gone. 0.55 moves the TypeScript language API to the top-level typescript namespace, and by 0.57 the old monaco.languages.typescript is an empty stub marked deprecated. setup.ts and EditorPane now use monaco.typescript. 0.56 reorganises the ESM tree behind an exports map that sends monaco-editor/ to esm/vs/.js. The language workers now come from languages/features//.worker, rather than the esm/vs/language re-exports the upstream docs still show, and the editor worker from editor/editor.worker. SuggestAdapter is still exported from languages/features/typescript/tsMode, so the playground keeps its subclass that adds quotes as completion trigger characters, and the module declaration in src/typings follows the new path. The component is unit-tested against a mocked monaco-editor under happy-dom, opted into per file, so apps/playground gains happy-dom, @testing-library/react and its peer @testing-library/dom as catalog devDependencies. The playground AGENTS.md and testing-strategy.md describe the component, the import paths and the DOM tests. Co-Authored-By: Claude Opus 5.5 --- .agents/docs/architecture/testing-strategy.md | 46 ++-- apps/playground/AGENTS.md | 51 +++- apps/playground/package.json | 6 +- .../src/components/Editor/EditorPane.tsx | 13 +- .../src/components/Editor/MonacoEditor.tsx | 85 ++++++ .../Editor/__tests__/MonacoEditor.test.tsx | 161 ++++++++++++ .../playground/src/components/Editor/setup.ts | 243 +++++++++--------- .../playground/src/typings/monaco-editor.d.ts | 2 +- pnpm-lock.yaml | 62 ++--- 9 files changed, 457 insertions(+), 212 deletions(-) create mode 100644 apps/playground/src/components/Editor/MonacoEditor.tsx create mode 100644 apps/playground/src/components/Editor/__tests__/MonacoEditor.test.tsx diff --git a/.agents/docs/architecture/testing-strategy.md b/.agents/docs/architecture/testing-strategy.md index c8e86ad79..4d8417fbb 100644 --- a/.agents/docs/architecture/testing-strategy.md +++ b/.agents/docs/architecture/testing-strategy.md @@ -31,29 +31,29 @@ path exercised against a real database is Playwright. Twenty-one. Scope a run with `pnpm exec vitest --project `; the name is the `name` field in that package's config, which is **not** always the directory name. -| Project | Package | Notable config | -| ------------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `api` | `apps/api` | libnest SWC plugin, confined to `apps/api` sources; `globals: true` | -| `web` | `apps/web` | `environment: 'happy-dom'`; redeclares the `@` alias | -| `gateway` | `apps/gateway` | node environment; redeclares the `@` alias. Request schemas, `resolveLanguage`, and the HTML `src/entry-server.tsx` renders — the routers and hydration are covered by `testing/src/specs/gateway-*.spec.ts`. A component test opts into happy-dom per file with a `// @vitest-environment happy-dom` docblock | -| `instrument-bundler` | `packages/instrument-bundler` | | -| `instrument-library` | `packages/instrument-library` | `environment: 'happy-dom'`; `resolve.alias` maps `/runtime/v1/@[/]` to `vendor/@/src/` and `/runtime/v1/@opendatacapture/runtime-core` to `packages/runtime-core/src`, so no `runtime/v1` build is needed. Tests go in top-level `src/__tests__/` only — see `packages/instrument-library/AGENTS.md` | -| `instrument-interpreter` | `packages/instrument-interpreter` | | -| `instrument-stubs` | `packages/instrument-stubs` | node environment; tests are `.test.js` (no tsconfig, so eslint cannot parse a `.ts` file here) | -| `instrument-utils` | `packages/instrument-utils` | | -| `playground` | `apps/playground` | node environment; redeclares the `@` alias. Pure code under `src/preview/`, plus components rendered to markup with `react-dom/server` (`src/components/Resizable/`) — the preview frame, its origin split, the message bridge and the editor/preview split are covered by `testing/src/specs/playground.spec.ts` | -| `playground-url` | `packages/playground-url` | | -| `react-core` | `packages/react-core` | `environment: 'happy-dom'` | -| `release-info` | `packages/release-info` | | -| `runtime-bundler` | `packages/runtime-bundler` | | -| `runtime-core` | `packages/runtime-core` | node environment, `// @vitest-environment happy-dom` per file for the browser branches of `i18n.ts`; relative imports carry `.js` (Node16). `*.test-d.ts` are checked by `tsc` in `lint`, not vitest | -| `runtime-internal` | `packages/runtime-internal` | node environment; `bootstrap.js` and `worker.js` are tested against stubbed browser globals — see `packages/runtime-internal/AGENTS.md` | -| `runtime-meta` | `packages/runtime-meta` | | -| `schemas` | `packages/schemas` | | -| `serve-instrument` | `packages/serve-instrument` | node environment; `test/` mocks `fs.watch` (never closed by `Server.stop()`, see `packages/serve-instrument/AGENTS.md`) and stubs the built `client.js`/`__TAILWIND_STYLES__` that only exist post-build | -| `subject-utils` | `packages/subject-utils` | | -| `vite-plugin-runtime` | `packages/vite-plugin-runtime` | | -| `runtime-v1` | `runtime/v1` | directory is `v1`, project is `runtime-v1` | +| Project | Package | Notable config | +| ------------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `api` | `apps/api` | libnest SWC plugin, confined to `apps/api` sources; `globals: true` | +| `web` | `apps/web` | `environment: 'happy-dom'`; redeclares the `@` alias | +| `gateway` | `apps/gateway` | node environment; redeclares the `@` alias. Request schemas, `resolveLanguage`, and the HTML `src/entry-server.tsx` renders — the routers and hydration are covered by `testing/src/specs/gateway-*.spec.ts`. A component test opts into happy-dom per file with a `// @vitest-environment happy-dom` docblock | +| `instrument-bundler` | `packages/instrument-bundler` | | +| `instrument-library` | `packages/instrument-library` | `environment: 'happy-dom'`; `resolve.alias` maps `/runtime/v1/@[/]` to `vendor/@/src/` and `/runtime/v1/@opendatacapture/runtime-core` to `packages/runtime-core/src`, so no `runtime/v1` build is needed. Tests go in top-level `src/__tests__/` only — see `packages/instrument-library/AGENTS.md` | +| `instrument-interpreter` | `packages/instrument-interpreter` | | +| `instrument-stubs` | `packages/instrument-stubs` | node environment; tests are `.test.js` (no tsconfig, so eslint cannot parse a `.ts` file here) | +| `instrument-utils` | `packages/instrument-utils` | | +| `playground` | `apps/playground` | node environment; redeclares the `@` alias. Pure code under `src/preview/`, plus components rendered to markup with `react-dom/server` (`src/components/Resizable/`) — the preview frame, its origin split, the message bridge and the editor/preview split are covered by `testing/src/specs/playground.spec.ts`. A component test opts into happy-dom per file with a `// @vitest-environment happy-dom` docblock (`src/components/Editor/__tests__/MonacoEditor.test.tsx`, against a mocked `monaco-editor`) | +| `playground-url` | `packages/playground-url` | | +| `react-core` | `packages/react-core` | `environment: 'happy-dom'` | +| `release-info` | `packages/release-info` | | +| `runtime-bundler` | `packages/runtime-bundler` | | +| `runtime-core` | `packages/runtime-core` | node environment, `// @vitest-environment happy-dom` per file for the browser branches of `i18n.ts`; relative imports carry `.js` (Node16). `*.test-d.ts` are checked by `tsc` in `lint`, not vitest | +| `runtime-internal` | `packages/runtime-internal` | node environment; `bootstrap.js` and `worker.js` are tested against stubbed browser globals — see `packages/runtime-internal/AGENTS.md` | +| `runtime-meta` | `packages/runtime-meta` | | +| `schemas` | `packages/schemas` | | +| `serve-instrument` | `packages/serve-instrument` | node environment; `test/` mocks `fs.watch` (never closed by `Server.stop()`, see `packages/serve-instrument/AGENTS.md`) and stubs the built `client.js`/`__TAILWIND_STYLES__` that only exist post-build | +| `subject-utils` | `packages/subject-utils` | | +| `vite-plugin-runtime` | `packages/vite-plugin-runtime` | | +| `runtime-v1` | `runtime/v1` | directory is `v1`, project is `runtime-v1` | Everything else has no unit tests and no way to run them: `apps/outreach` and `packages/{demo, instrument-guidelines, licenses}`. `testing/`, `storybook/` and `vendor/**` fall outside the diff --git a/apps/playground/AGENTS.md b/apps/playground/AGENTS.md index a213815b1..32fba6cec 100644 --- a/apps/playground/AGENTS.md +++ b/apps/playground/AGENTS.md @@ -8,18 +8,13 @@ Read the root `AGENTS.md` first for the rules that apply everywhere. ## Top-level await is load-bearing -Two modules do real work at import time, and both are fragile. - -- **`src/pages/IndexPage.tsx`** calls `await initialize({ wasmURL })` from `esbuild-wasm` above the - component. esbuild-wasm throws `Cannot call "initialize" more than once` on a second call, so this - must stay in exactly one module. `src/App.tsx` reaches it through `React.lazy`, which is what puts - the 13 MB `esbuild.wasm` download behind the Suspense fallback. Importing `IndexPage` eagerly, or - adding a second `initialize()`, breaks the app at boot. -- **`src/components/Editor/setup.ts`** configures Monaco (workers, compiler options, themes, prettier - as the formatter) inside a top-level `await loader.init()` block. `Editor.tsx` pulls it in with a - bare `import './setup'` for the side effect. - -Both depend on `build.target` and `optimizeDeps.esbuildOptions.target` being `es2022` in +**`src/pages/IndexPage.tsx`** calls `await initialize({ wasmURL })` from `esbuild-wasm` above the +component. esbuild-wasm throws `Cannot call "initialize" more than once` on a second call, so this +must stay in exactly one module. `src/App.tsx` reaches it through `React.lazy`, which is what puts +the 13 MB `esbuild.wasm` download behind the Suspense fallback. Importing `IndexPage` eagerly, or +adding a second `initialize()`, breaks the app at boot. + +It depends on `build.target` and `optimizeDeps.esbuildOptions.target` being `es2022` in `vite.config.ts`. Lowering either one is how top-level await silently stops compiling. `rollupOptions.external: ['esbuild']` is also required. `packages/instrument-bundler/src/vendor/esbuild.ts` @@ -27,6 +22,31 @@ picks between `esbuild` and `esbuild-wasm` on `typeof window`; without the exter pull the Node package into the browser bundle. Background: `.agents/docs/architecture/runtime-and-vendor.md`. +## Monaco is wired up by hand + +**`src/components/Editor/setup.ts` configures Monaco at import time** — workers, compiler options, +themes, prettier as the formatter. `Editor.tsx` pulls it in with a bare `import './setup'` for the +side effect, so it has run before any editor exists. + +**`src/components/Editor/MonacoEditor.tsx` is the editor component**, in place of +`@monaco-editor/react`, whose 4.7 types import `monaco-editor/esm/vs/editor/editor.api`, a path the +`exports` map `monaco-editor` 0.56 added no longer resolves. It keeps one model per `path` and never +disposes one: `EditorPane` is keyed by instrument and filename, so every file switch remounts the +editor, and the surviving model is what keeps the file's content and undo history. It restores each +model's cursor and scroll position on the way back. Only `EditorPane` disposes models: all of them, +on an instrument switch or when it unmounts. + +**Deep imports go through that `exports` map**, which sends `monaco-editor/` to +`esm/vs/.js`. The language workers come from +`monaco-editor/languages/features//.worker`; the `esm/vs/language/*` paths the +Monaco docs still show are deprecated re-exports. `SuggestAdapter` comes from +`monaco-editor/languages/features/typescript/tsMode`, an internal module with no types of its own, +declared in `src/typings/monaco-editor.d.ts`. `setup.ts` switches off the built-in completion +provider (`completionItems: false`) and registers a subclass whose only change is quotes as trigger +characters, so string-literal completions open as you type. The TypeScript API is the top-level +`monaco.typescript` namespace; `monaco.languages.typescript` has been an empty `{ deprecated: true }` +since 0.57. + ## Running it `pnpm dev` at the repo root does **not** start the playground — `dev:core` filters to api, gateway and @@ -167,8 +187,11 @@ and covers only what is pure — `src/preview/__tests__/protocol.test.ts` pins t error serialization and `resolvePreviewOrigin`, and `config.test.ts` the `/config.json` contract. A component that renders on a server can still be pinned through `react-dom/server`'s `renderToStaticMarkup`, as `src/components/Resizable/__tests__/` does for the attributes its classes -key on. Anything that touches esbuild-wasm, Monaco workers or `/runtime/v1` has no test environment -here; the frame itself, the origin split, the runtime config, the message bridge and the resizable +key on. A component test that needs a DOM opts in per file with a `// @vitest-environment happy-dom` +docblock on its first line and renders with `@testing-library/react`; +`src/components/Editor/__tests__/MonacoEditor.test.tsx` does, against a mocked `monaco-editor`, +because Monaco itself does not run under happy-dom. Anything that touches esbuild-wasm, Monaco +workers or `/runtime/v1` has no test environment here; the frame itself, the origin split, the runtime config, the message bridge and the resizable editor/preview split are exercised for real by `testing/src/specs/playground.spec.ts`, which Playwright runs against this app's dev server. diff --git a/apps/playground/package.json b/apps/playground/package.json index cf57fb306..e385b1a36 100644 --- a/apps/playground/package.json +++ b/apps/playground/package.json @@ -16,7 +16,6 @@ "@douglasneuroinformatics/libcrypto": "catalog:", "@douglasneuroinformatics/libjs": "catalog:", "@douglasneuroinformatics/libui": "catalog:", - "@monaco-editor/react": "^4.7.0", "@opendatacapture/instrument-bundler": "workspace:*", "@opendatacapture/playground-url": "workspace:*", "@opendatacapture/react-core": "workspace:*", @@ -32,7 +31,7 @@ "jwt-decode": "^4.0.0", "lodash-es": "workspace:lodash-es__4.x@*", "lucide-react": "^1.49.0", - "monaco-editor": "^0.54.0", + "monaco-editor": "^0.57.0", "motion": "catalog:", "neverthrow": "catalog:", "react": "workspace:react__19.x@*", @@ -48,7 +47,10 @@ "@opendatacapture/vite-plugin-runtime": "workspace:*", "@storybook/react-vite": "catalog:", "@tailwindcss/vite": "catalog:", + "@testing-library/dom": "catalog:", + "@testing-library/react": "catalog:", "@vitejs/plugin-react-swc": "^4.3.3", + "happy-dom": "catalog:", "tailwindcss": "catalog:", "type-fest": "workspace:type-fest__4.x@*", "vite": "catalog:" diff --git a/apps/playground/src/components/Editor/EditorPane.tsx b/apps/playground/src/components/Editor/EditorPane.tsx index 8b581c212..6fafbe5fb 100644 --- a/apps/playground/src/components/Editor/EditorPane.tsx +++ b/apps/playground/src/components/Editor/EditorPane.tsx @@ -1,7 +1,6 @@ import { useEffect, useRef, useState } from 'react'; import { useTheme, useTranslation } from '@douglasneuroinformatics/libui/hooks'; -import MonacoEditor from '@monaco-editor/react'; import { useEditorErrorSync } from '@/hooks/useEditorErrorSync'; import { useFilesRef } from '@/hooks/useFilesRef'; @@ -11,6 +10,7 @@ import { useAppStore } from '@/store'; import { getImageMIMEType, inferFileType, isBase64EncodedFileType } from '@/utils/file'; import { EditorPanePlaceholder } from './EditorPanePlaceholder'; +import { MonacoEditor } from './MonacoEditor'; import type { MonacoEditorType, MonacoType } from './types'; @@ -39,8 +39,8 @@ export const EditorPane = () => { Object.keys(libs).forEach((filename) => { const uri = monaco.Uri.parse(filename); if (!monaco.editor.getModel(uri)) { - monaco.languages.typescript.javascriptDefaults.addExtraLib(libs[filename]!, filename); - monaco.languages.typescript.typescriptDefaults.addExtraLib(libs[filename]!, filename); + monaco.typescript.javascriptDefaults.addExtraLib(libs[filename]!, filename); + monaco.typescript.typescriptDefaults.addExtraLib(libs[filename]!, filename); monaco.editor.createModel(libs[filename]!, 'typescript', uri); } }); @@ -78,7 +78,7 @@ export const EditorPane = () => { if (monaco.editor.getModel(uri)) { continue; } else if (fileType === 'typescript') { - monaco.languages.typescript.typescriptDefaults.addExtraLib(file.content, file.name); + monaco.typescript.typescriptDefaults.addExtraLib(file.content, file.name); monaco.editor.createModel(file.content, 'typescript', uri); } else if (fileType === 'javascript') { monaco.editor.createModel(file.content, 'javascript', uri); @@ -131,10 +131,9 @@ export const EditorPane = () => { return ( { path={defaultFile.name} theme={`odc-${theme}`} onChange={(value) => { - setSelectedFileContent(value ?? ''); + setSelectedFileContent(value); }} onMount={handleEditorDidMount} /> diff --git a/apps/playground/src/components/Editor/MonacoEditor.tsx b/apps/playground/src/components/Editor/MonacoEditor.tsx new file mode 100644 index 000000000..93f0361e0 --- /dev/null +++ b/apps/playground/src/components/Editor/MonacoEditor.tsx @@ -0,0 +1,85 @@ +import { useEffect, useRef } from 'react'; + +import * as monaco from 'monaco-editor'; + +import type { MonacoEditorType, MonacoType } from './types'; + +const viewStates = new WeakMap(); + +const saveViewState = (editor: MonacoEditorType) => { + const model = editor.getModel(); + if (model) { + viewStates.set(model, editor.saveViewState()); + } +}; + +const getOrCreateModel = (path: string, defaultValue: string, defaultLanguage: string) => { + const uri = monaco.Uri.parse(path); + return monaco.editor.getModel(uri) ?? monaco.editor.createModel(defaultValue, defaultLanguage, uri); +}; + +export type MonacoEditorProps = { + className?: string; + defaultLanguage: string; + defaultValue: string; + onChange: (value: string) => void; + onMount: (editor: MonacoEditorType, monaco: MonacoType) => void; + options: monaco.editor.IEditorOptions & monaco.editor.IGlobalEditorOptions; + path: string; + theme: string; +}; + +/** + * Shows the model of `path`, creating it from `defaultValue` the first time. Models are never disposed + * here, so a file keeps its content and undo history when the editor switches away or unmounts. + */ +export const MonacoEditor = ({ + className, + defaultLanguage, + defaultValue, + onChange, + onMount, + options, + path, + theme +}: MonacoEditorProps) => { + const containerRef = useRef(null); + const editorRef = useRef(null); + + useEffect(() => { + const editor = monaco.editor.create(containerRef.current!, { ...options, model: null }); + editorRef.current = editor; + return () => { + saveViewState(editor); + editor.dispose(); + }; + }, []); + + useEffect(() => { + const editor = editorRef.current!; + saveViewState(editor); + const model = getOrCreateModel(path, defaultValue, defaultLanguage); + editor.setModel(model); + editor.restoreViewState(viewStates.get(model) ?? null); + }, [path]); + + useEffect(() => { + onMount(editorRef.current!, monaco); + }, []); + + useEffect(() => { + editorRef.current!.updateOptions(options); + }, [options]); + + useEffect(() => { + monaco.editor.setTheme(theme); + }, [theme]); + + useEffect(() => { + const editor = editorRef.current!; + const subscription = editor.onDidChangeModelContent(() => onChange(editor.getValue())); + return () => subscription.dispose(); + }, [onChange]); + + return
; +}; diff --git a/apps/playground/src/components/Editor/__tests__/MonacoEditor.test.tsx b/apps/playground/src/components/Editor/__tests__/MonacoEditor.test.tsx new file mode 100644 index 000000000..c180889cd --- /dev/null +++ b/apps/playground/src/components/Editor/__tests__/MonacoEditor.test.tsx @@ -0,0 +1,161 @@ +// @vitest-environment happy-dom + +import { cleanup, render } from '@testing-library/react'; +import * as monaco from 'monaco-editor'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { MonacoEditor } from '../MonacoEditor'; + +import type { MonacoEditorProps } from '../MonacoEditor'; +import type { MonacoEditorType } from '../types'; + +type FakeModel = { dispose: () => void; language: string; uri: string; value: string }; + +const fakeMonaco = vi.hoisted(() => { + const models = new Map(); + + const createFakeEditor = () => { + let model: FakeModel | null = null; + const contentListeners = new Set<() => void>(); + return { + dispose: vi.fn(), + getModel: () => model, + getValue: () => model!.value, + onDidChangeModelContent: (listener: () => void) => { + contentListeners.add(listener); + return { dispose: () => contentListeners.delete(listener) }; + }, + restoreViewState: vi.fn(), + saveViewState: () => ({ savedFrom: model!.uri }), + setModel: vi.fn((nextModel: FakeModel) => { + model = nextModel; + }), + type: (value: string) => { + model!.value = value; + contentListeners.forEach((listener) => listener()); + }, + updateOptions: vi.fn() + }; + }; + + return { + createFakeEditor, + editor: { + create: vi.fn((_container: HTMLElement, _options: object) => createFakeEditor()), + createModel: vi.fn((value: string, language: string, uri: string): FakeModel => { + const model = { dispose: vi.fn(), language, uri, value }; + models.set(uri, model); + return model; + }), + getModel: (uri: string) => models.get(uri) ?? null, + setTheme: vi.fn() + }, + models, + Uri: { parse: (path: string) => path } + }; +}); + +vi.mock('monaco-editor', () => ({ editor: fakeMonaco.editor, Uri: fakeMonaco.Uri })); + +type FakeEditor = ReturnType; + +const defaultProps: MonacoEditorProps = { + defaultLanguage: 'typescript', + defaultValue: 'export {};', + onChange: vi.fn(), + onMount: vi.fn(), + options: { tabSize: 2 }, + path: 'index.ts', + theme: 'odc-light' +}; + +const renderEditor = (props: Partial = {}) => { + const view = render(); + const editor = fakeMonaco.editor.create.mock.results.at(-1)!.value as FakeEditor; + const rerender = (nextProps: Partial) => { + view.rerender(); + }; + return { editor, rerender, unmount: view.unmount }; +}; + +describe('MonacoEditor', () => { + beforeEach(() => { + fakeMonaco.models.clear(); + vi.clearAllMocks(); + }); + + afterEach(() => { + cleanup(); + }); + + it('should create the model of a new path from the default value, in the default language', () => { + const { editor } = renderEditor(); + expect(fakeMonaco.editor.createModel).toHaveBeenCalledExactlyOnceWith('export {};', 'typescript', 'index.ts'); + expect(editor.getModel()).toBe(fakeMonaco.models.get('index.ts')); + }); + + it('should reuse the model a path already has, so the file keeps its content and undo history', () => { + const existingModel = fakeMonaco.editor.createModel('const x = 1;', 'typescript', 'index.ts'); + fakeMonaco.editor.createModel.mockClear(); + const { editor } = renderEditor(); + expect(fakeMonaco.editor.createModel).not.toHaveBeenCalled(); + expect(editor.getModel()).toBe(existingModel); + }); + + it('should swap the editor to the model of the new path when the path changes', () => { + const { editor, rerender } = renderEditor(); + rerender({ defaultLanguage: 'css', defaultValue: 'body {}', path: 'styles.css' }); + expect(fakeMonaco.editor.create).toHaveBeenCalledOnce(); + expect(editor.getModel()).toMatchObject({ language: 'css', uri: 'styles.css', value: 'body {}' }); + }); + + it('should restore the view state a model had when its file was last shown', () => { + const { editor, rerender } = renderEditor(); + rerender({ path: 'other.ts' }); + rerender({ path: 'index.ts' }); + expect(editor.restoreViewState).toHaveBeenLastCalledWith({ savedFrom: 'index.ts' }); + }); + + it('should never dispose a model, neither on a path change nor on unmount', () => { + const { rerender, unmount } = renderEditor(); + rerender({ path: 'other.ts' }); + unmount(); + fakeMonaco.models.forEach((model) => expect(model.dispose).not.toHaveBeenCalled()); + }); + + it('should create the editor with the options and update it when they change', () => { + const { editor, rerender } = renderEditor(); + expect(fakeMonaco.editor.create).toHaveBeenCalledWith(expect.any(HTMLDivElement), { model: null, tabSize: 2 }); + rerender({ options: { tabSize: 4 } }); + expect(editor.updateOptions).toHaveBeenLastCalledWith({ tabSize: 4 }); + }); + + it('should set the theme, and set it again when it changes', () => { + const { rerender } = renderEditor(); + expect(fakeMonaco.editor.setTheme).toHaveBeenLastCalledWith('odc-light'); + rerender({ theme: 'odc-dark' }); + expect(fakeMonaco.editor.setTheme).toHaveBeenLastCalledWith('odc-dark'); + }); + + it('should call onMount once, with the editor already showing its model', () => { + const onMount = vi.fn((editor: MonacoEditorType) => expect(editor.getModel()).not.toBeNull()); + const { editor, rerender } = renderEditor({ onMount }); + rerender({ options: { tabSize: 4 }, path: 'other.ts' }); + expect(onMount).toHaveBeenCalledExactlyOnceWith(editor, monaco); + }); + + it('should pass the content to the latest onChange on every edit', () => { + const onChange = vi.fn(); + const { editor, rerender } = renderEditor(); + rerender({ onChange }); + editor.type('export const x = 1;'); + expect(onChange).toHaveBeenCalledExactlyOnceWith('export const x = 1;'); + expect(defaultProps.onChange).not.toHaveBeenCalled(); + }); + + it('should dispose the editor on unmount', () => { + const { editor, unmount } = renderEditor(); + unmount(); + expect(editor.dispose).toHaveBeenCalledOnce(); + }); +}); diff --git a/apps/playground/src/components/Editor/setup.ts b/apps/playground/src/components/Editor/setup.ts index 7cd604f0d..e928ca1ec 100644 --- a/apps/playground/src/components/Editor/setup.ts +++ b/apps/playground/src/components/Editor/setup.ts @@ -1,12 +1,11 @@ -import { loader } from '@monaco-editor/react'; import envTypes from '@opendatacapture/runtime-v1/env?raw'; import * as monaco from 'monaco-editor'; -import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'; -import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'; -import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'; -import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'; -import TypeScriptWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'; -import { SuggestAdapter } from 'monaco-editor/esm/vs/language/typescript/tsMode'; +import EditorWorker from 'monaco-editor/editor/editor.worker?worker'; +import CssWorker from 'monaco-editor/languages/features/css/css.worker?worker'; +import HtmlWorker from 'monaco-editor/languages/features/html/html.worker?worker'; +import JsonWorker from 'monaco-editor/languages/features/json/json.worker?worker'; +import TypeScriptWorker from 'monaco-editor/languages/features/typescript/ts.worker?worker'; +import { SuggestAdapter } from 'monaco-editor/languages/features/typescript/tsMode'; import prettierPluginBabel from 'prettier/plugins/babel'; import prettierPluginEstree from 'prettier/plugins/estree'; import prettier from 'prettier/standalone'; @@ -32,125 +31,119 @@ self.MonacoEnvironment = { } }; -loader.config({ monaco }); - -{ - const monaco = await loader.init(); - - const defaults: monaco.languages.typescript.ModeConfiguration = { - codeActions: true, - completionItems: false, - definitions: true, - diagnostics: true, - documentHighlights: true, - documentRangeFormattingEdits: true, - documentSymbols: true, - hovers: true, - inlayHints: true, - onTypeFormattingEdits: true, - references: true, - rename: true, - signatureHelp: true - }; - - monaco.languages.typescript.javascriptDefaults.setModeConfiguration(defaults); - monaco.languages.typescript.typescriptDefaults.setModeConfiguration(defaults); - - // eslint-disable-next-line @typescript-eslint/no-misused-promises - monaco.languages.onLanguage('typescript', async () => { - const worker = await monaco.languages.typescript.getTypeScriptWorker(); - monaco.languages.registerCompletionItemProvider('typescript', new TypeScriptSuggestAdapter(worker)); - }); - - // eslint-disable-next-line @typescript-eslint/no-misused-promises - monaco.languages.onLanguage('javascript', async () => { - const worker = await monaco.languages.typescript.getJavaScriptWorker(); - monaco.languages.registerCompletionItemProvider('javascript', new TypeScriptSuggestAdapter(worker)); - }); - - /** - * Setup the TypeScript compiler options as similarly as possible to - * the project setup. This is limited by an absence of modern options - * (e.g., module resolution) in the monaco enum options. - */ - - const baseTypeScriptOptions: monaco.languages.typescript.CompilerOptions = { - allowJs: true, - allowSyntheticDefaultImports: false, - checkJs: true, - forceConsistentCasingInFileNames: true, - isolatedModules: true, - jsx: monaco.languages.typescript.JsxEmit.ReactJSX, - jsxImportSource: '/runtime/v1/react@19.x', - module: monaco.languages.typescript.ModuleKind.ESNext, - moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs, - newLine: monaco.languages.typescript.NewLineKind.LineFeed, - strict: false, - target: monaco.languages.typescript.ScriptTarget.ES2020 - }; - - monaco.languages.typescript.javascriptDefaults.setCompilerOptions(baseTypeScriptOptions); - monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ - ...baseTypeScriptOptions, - allowImportingTsExtensions: true, - strict: true - }); - - /** - * Define custom light and dark themes for the editor - */ - monaco.editor.defineTheme('odc-light', { - base: 'vs', - colors: { - // slate-50 - 'editor.background': '#F8FAFC' - }, - inherit: true, - rules: [] - }); - - monaco.editor.defineTheme('odc-dark', { - base: 'vs-dark', - colors: { - // slate-800 - 'editor.background': '#1E293B' - }, - inherit: true, - rules: [] - }); - - /** - * Configure the monaco instance to use prettier as the document formatter. - * The keyboard shortcut to format the document is set to Alt + F. - */ - monaco.editor.addKeybindingRule({ - command: 'editor.action.formatDocument', - keybinding: monaco.KeyMod.Alt | monaco.KeyCode.KeyF - }); - - const documentFormattingEditProvider: monaco.languages.DocumentFormattingEditProvider = { - async provideDocumentFormattingEdits(model) { - const range = model.getFullModelRange(); - const value = model.getValue(); - const text = await prettier.format(value, { - parser: 'babel-ts', - plugins: [prettierPluginBabel, prettierPluginEstree], - printWidth: 120, - singleQuote: true, - trailingComma: 'none' - }); - return [{ range, text }]; - } - }; +const defaults: monaco.typescript.ModeConfiguration = { + codeActions: true, + completionItems: false, + definitions: true, + diagnostics: true, + documentHighlights: true, + documentRangeFormattingEdits: true, + documentSymbols: true, + hovers: true, + inlayHints: true, + onTypeFormattingEdits: true, + references: true, + rename: true, + signatureHelp: true +}; + +monaco.typescript.javascriptDefaults.setModeConfiguration(defaults); +monaco.typescript.typescriptDefaults.setModeConfiguration(defaults); + +// eslint-disable-next-line @typescript-eslint/no-misused-promises +monaco.languages.onLanguage('typescript', async () => { + const worker = await monaco.typescript.getTypeScriptWorker(); + monaco.languages.registerCompletionItemProvider('typescript', new TypeScriptSuggestAdapter(worker)); +}); + +// eslint-disable-next-line @typescript-eslint/no-misused-promises +monaco.languages.onLanguage('javascript', async () => { + const worker = await monaco.typescript.getJavaScriptWorker(); + monaco.languages.registerCompletionItemProvider('javascript', new TypeScriptSuggestAdapter(worker)); +}); + +/** + * Setup the TypeScript compiler options as similarly as possible to + * the project setup. This is limited by an absence of modern options + * (e.g., module resolution) in the monaco enum options. + */ + +const baseTypeScriptOptions: monaco.typescript.CompilerOptions = { + allowJs: true, + allowSyntheticDefaultImports: false, + checkJs: true, + forceConsistentCasingInFileNames: true, + isolatedModules: true, + jsx: monaco.typescript.JsxEmit.ReactJSX, + jsxImportSource: '/runtime/v1/react@19.x', + module: monaco.typescript.ModuleKind.ESNext, + moduleResolution: monaco.typescript.ModuleResolutionKind.NodeJs, + newLine: monaco.typescript.NewLineKind.LineFeed, + strict: false, + target: monaco.typescript.ScriptTarget.ES2020 +}; - monaco.languages.registerDocumentFormattingEditProvider('javascript', documentFormattingEditProvider); - monaco.languages.registerDocumentFormattingEditProvider('typescript', documentFormattingEditProvider); +monaco.typescript.javascriptDefaults.setCompilerOptions(baseTypeScriptOptions); +monaco.typescript.typescriptDefaults.setCompilerOptions({ + ...baseTypeScriptOptions, + allowImportingTsExtensions: true, + strict: true +}); + +/** + * Define custom light and dark themes for the editor + */ +monaco.editor.defineTheme('odc-light', { + base: 'vs', + colors: { + // slate-50 + 'editor.background': '#F8FAFC' + }, + inherit: true, + rules: [] +}); + +monaco.editor.defineTheme('odc-dark', { + base: 'vs-dark', + colors: { + // slate-800 + 'editor.background': '#1E293B' + }, + inherit: true, + rules: [] +}); + +/** + * Configure the monaco instance to use prettier as the document formatter. + * The keyboard shortcut to format the document is set to Alt + F. + */ +monaco.editor.addKeybindingRule({ + command: 'editor.action.formatDocument', + keybinding: monaco.KeyMod.Alt | monaco.KeyCode.KeyF +}); + +const documentFormattingEditProvider: monaco.languages.DocumentFormattingEditProvider = { + async provideDocumentFormattingEdits(model) { + const range = model.getFullModelRange(); + const value = model.getValue(); + const text = await prettier.format(value, { + parser: 'babel-ts', + plugins: [prettierPluginBabel, prettierPluginEstree], + printWidth: 120, + singleQuote: true, + trailingComma: 'none' + }); + return [{ range, text }]; + } +}; - // Other - monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true); +monaco.languages.registerDocumentFormattingEditProvider('javascript', documentFormattingEditProvider); +monaco.languages.registerDocumentFormattingEditProvider('typescript', documentFormattingEditProvider); - const uri = monaco.Uri.parse('globals.d.ts'); +// Other +monaco.typescript.typescriptDefaults.setEagerModelSync(true); - monaco.languages.typescript.typescriptDefaults.addExtraLib(envTypes, 'globals.d.ts'); - monaco.editor.createModel(envTypes, 'typescript', uri); -} +const uri = monaco.Uri.parse('globals.d.ts'); + +monaco.typescript.typescriptDefaults.addExtraLib(envTypes, 'globals.d.ts'); +monaco.editor.createModel(envTypes, 'typescript', uri); diff --git a/apps/playground/src/typings/monaco-editor.d.ts b/apps/playground/src/typings/monaco-editor.d.ts index 20df33d3f..f762c5276 100644 --- a/apps/playground/src/typings/monaco-editor.d.ts +++ b/apps/playground/src/typings/monaco-editor.d.ts @@ -1,4 +1,4 @@ -declare module 'monaco-editor/esm/vs/language/typescript/tsMode' { +declare module 'monaco-editor/languages/features/typescript/tsMode' { export class SuggestAdapter { triggerCharacters?: string[]; constructor(worker: any): void; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cce5f98a6..1740464b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -547,9 +547,6 @@ importers: '@douglasneuroinformatics/libui': specifier: 'catalog:' version: 6.16.1(immer@11.1.18)(neverthrow@8.2.0)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(tailwindcss@4.3.3)(use-sync-external-store@1.6.0(react@vendor+react@19.x))(zod@vendor+zod@3.x) - '@monaco-editor/react': - specifier: ^4.7.0 - version: 4.7.0(monaco-editor@0.54.0)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) '@opendatacapture/instrument-bundler': specifier: workspace:* version: link:../../packages/instrument-bundler @@ -596,8 +593,8 @@ importers: specifier: ^1.49.0 version: 1.49.0(react@vendor+react@19.x) monaco-editor: - specifier: ^0.54.0 - version: 0.54.0 + specifier: ^0.57.0 + version: 0.57.0 motion: specifier: 'catalog:' version: 13.4.4(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) @@ -638,9 +635,18 @@ importers: '@tailwindcss/vite': specifier: 'catalog:' version: 4.3.3(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + '@testing-library/dom': + specifier: 'catalog:' + version: 10.4.2 + '@testing-library/react': + specifier: 'catalog:' + version: 16.3.3(@testing-library/dom@10.4.2)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) '@vitejs/plugin-react-swc': specifier: ^4.3.3 version: 4.3.3(@swc/helpers@0.5.23)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + happy-dom: + specifier: 'catalog:' + version: 20.11.15 tailwindcss: specifier: 'catalog:' version: 4.3.3 @@ -3246,16 +3252,6 @@ packages: '@microsoft/tsdoc@0.17.0': resolution: {integrity: sha512-p68VexhnH7ojf3U27RdryUDqnKJgKaWUbCL96vJR7N0mqDuhKG5OIECk3sCPPdynxjk+yoVhRkScqtv5KQjzsw==} - '@monaco-editor/loader@1.7.0': - resolution: {integrity: sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==} - - '@monaco-editor/react@4.7.0': - resolution: {integrity: sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==} - peerDependencies: - monaco-editor: '>= 0.25.0 < 1' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@mongodb-js/saslprep@1.4.11': resolution: {integrity: sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA==} @@ -6946,12 +6942,12 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} - dompurify@3.1.7: - resolution: {integrity: sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==} - dompurify@3.4.12: resolution: {integrity: sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==} + dompurify@3.4.15: + resolution: {integrity: sha512-EUBjM+B+lkDE41iE82DDSCfkoPGfXx8IxFxPMjNzm/Uk4xDet77rTN9wqlxlVg71kK7XGuUMv6wUxJUwwv+Xyw==} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -8624,8 +8620,8 @@ packages: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} - monaco-editor@0.54.0: - resolution: {integrity: sha512-hx45SEUoLatgWxHKCmlLJH81xBo0uXP4sRkESUpmDQevfi+e7K1VuiSprK6UpQ8u4zOcKNiH0pMvHvlMWA/4cw==} + monaco-editor@0.57.0: + resolution: {integrity: sha512-5BkI9KGoqrNvBGUe15/QlZq3OooZ8WLg1AxTpaqHRCP3HNpzPPZKE2EDz8M7c+VRmCeUw1Brp4cx/PWm3kI/5A==} mongodb-connection-string-url@7.0.2: resolution: {integrity: sha512-ZoS07RoFqpKYQwAk59qmrx8+jJHNHU30UjlU96QktiGn1ltvDr+vCznLX5DiUBLEpMAHatHNWV1nM/74ul66kA==} @@ -9927,9 +9923,6 @@ packages: resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} engines: {node: '>=6'} - state-local@1.0.7: - resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} - statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -12716,17 +12709,6 @@ snapshots: '@microsoft/tsdoc@0.17.0': {} - '@monaco-editor/loader@1.7.0': - dependencies: - state-local: 1.0.7 - - '@monaco-editor/react@4.7.0(monaco-editor@0.54.0)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)': - dependencies: - '@monaco-editor/loader': 1.7.0 - monaco-editor: 0.54.0 - react: link:vendor/react@19.x - react-dom: link:vendor/react-dom@19.x - '@mongodb-js/saslprep@1.4.11': dependencies: sparse-bitfield: 3.0.3 @@ -16578,12 +16560,14 @@ snapshots: dependencies: domelementtype: 2.3.0 - dompurify@3.1.7: {} - dompurify@3.4.12: optionalDependencies: '@types/trusted-types': 2.0.7 + dompurify@3.4.15: + optionalDependencies: + '@types/trusted-types': 2.0.7 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -18913,9 +18897,9 @@ snapshots: minipass@7.1.3: {} - monaco-editor@0.54.0: + monaco-editor@0.57.0: dependencies: - dompurify: 3.1.7 + dompurify: 3.4.15 marked: 14.0.0 mongodb-connection-string-url@7.0.2: @@ -20434,8 +20418,6 @@ snapshots: dependencies: type-fest: 0.7.1 - state-local@1.0.7: {} - statuses@2.0.2: {} std-env@4.3.0: {} From 2bda8414e82c105437211e93bc5c601b1ef35993 Mon Sep 17 00:00:00 2001 From: joshunrau Date: Wed, 7 Oct 2026 14:31:51 -0400 Subject: [PATCH 14/14] build: upgrade vite to 8 The catalog moves vite from ^7.3.6 to ^8.3.1, the newest release past the 7-day release age (#1622), and the lockfile holds one copy of it for every workspace. Outreach keeps the Vite 6 that Astro brings. Every Vite plugin in use already accepts 8, so none of them moves. Vite 8 replaces esbuild and Rollup with Oxc and Rolldown, so the configs move to the new option names. optimizeDeps.esbuildOptions becomes optimizeDeps.rolldownOptions with an explicit transform.target of es2022, because the automatic conversion drops target. build.rollupOptions becomes build.rolldownOptions in web and playground, keeping the externals and the playground's multi-page input. Storybook's importer-keyed @ alias moves from the deprecated customResolver to a pre-enforced resolveId plugin that behaves the same way. The playground config's type import gains its extension, which silences the native config loader warning Vite 8 prints on every playground build. Vitest now runs on Vite 8 too, so tests are transformed by Oxc instead of esbuild. Oxc cannot lower the TC39 decorators in runtime-core's i18n.ts, and Node cannot run them, so six suites failed to load. runtime-core's vitest config gains a pre plugin that lowers them with esbuild, which already builds its dist, and instrument-library, whose alias runs that same source, uses the plugin as well. The api config moves its exclusion from the esbuild option, which Vite 8 ignores once vitest sets oxc, to oxc.exclude, so SWC alone still transforms the api's own files. Rolldown splits the web bundle differently. The entry now preloads 51 files instead of 2, and the first load grows by about 13 percent, or 50 KB gzipped, because modules such as table-core and react-dropzone move from lazy chunks into shared ones. This is a known effect that build.rolldownOptions.output.codeSplitting can tune later. Lightning CSS adds about 5 percent to the CSS. The AGENTS.md files and the react-core playbook follow the renamed options and the two plugins. Co-Authored-By: Claude Opus 5.5 --- .../docs/playbooks/promote-to-react-core.md | 2 +- apps/api/AGENTS.md | 6 +- apps/api/vitest.config.ts | 10 +- apps/gateway/vite.config.ts | 6 +- apps/playground/AGENTS.md | 6 +- apps/playground/vite.config.ts | 10 +- apps/web/vite.config.ts | 8 +- packages/instrument-library/AGENTS.md | 3 +- packages/instrument-library/vitest.config.ts | 2 + packages/runtime-core/AGENTS.md | 5 +- packages/runtime-core/vitest.config.ts | 25 + pnpm-lock.yaml | 480 ++++++++++++++---- pnpm-workspace.yaml | 2 +- storybook/AGENTS.md | 6 +- storybook/vite.config.js | 41 +- 15 files changed, 473 insertions(+), 139 deletions(-) diff --git a/.agents/docs/playbooks/promote-to-react-core.md b/.agents/docs/playbooks/promote-to-react-core.md index 743814565..fdba18fe2 100644 --- a/.agents/docs/playbooks/promote-to-react-core.md +++ b/.agents/docs/playbooks/promote-to-react-core.md @@ -62,7 +62,7 @@ so nothing that passed lint in `apps/web` fails on style here; what breaks is re 6. **Repoint every `@/…` import.** `packages/react-core/tsconfig.json` declares one path mapping, `/runtime/v1/*` — there is no `@/*` here, so every `@/…` specifier is a module-resolution error. - Storybook agrees: its `vite.config.js` resolves `@` through a `customResolver` keyed on the + Storybook agrees: its `vite.config.js` resolves `@` through a `source-root-alias` plugin keyed on the importing file and returns `null` for anything outside `apps/web/src` and `apps/playground/src`. `@/components/*` becomes a relative sibling import. Everything else — `@/store`, `@/hooks/*`, `@/config`, `@/services/*`, `@/utils/*` — does not move; it becomes a prop or callback per step 5. diff --git a/apps/api/AGENTS.md b/apps/api/AGENTS.md index ec418bb23..a9921157a 100644 --- a/apps/api/AGENTS.md +++ b/apps/api/AGENTS.md @@ -253,9 +253,9 @@ Provider overrides are deliberately not offered — nothing needs one yet. Addin The `apps/api/vitest.config.ts` `libnest` plugin is required — it applies the SWC decorator transforms, without which `@Injectable`/`@Body` metadata does not exist at runtime. The config -confines SWC to files under `apps/api` and leaves workspace packages to esbuild, as in every other -project. Letting SWC transform them too makes the merged coverage report count each of their -functions twice. +confines SWC to files under `apps/api` and leaves workspace packages to Vite's Oxc transform, as in +every other project; its `oxc.exclude` keeps Oxc off `apps/api` itself. Letting SWC transform them +too makes the merged coverage report count each of their functions twice. Ignore `apps/api/.vscode/test/*` — it is stale, never runs, and references modules that no longer exist. diff --git a/apps/api/vitest.config.ts b/apps/api/vitest.config.ts index b0c6b27d3..36464b388 100644 --- a/apps/api/vitest.config.ts +++ b/apps/api/vitest.config.ts @@ -15,10 +15,10 @@ const [swcPlugin, libnestPlugin] = libnest({ const apiFilePattern = new RegExp(`^${(import.meta.dirname + path.sep).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`); -// libnest exposes no include option and its SWC plugin disables esbuild outright, so SWC would also -// transform the workspace packages this project imports. SWC source maps place functions differently -// from the esbuild transform every other project uses, which makes merged coverage count each of those -// functions twice, with one copy never hit. SWC is only needed for the decorators in this app. +// libnest exposes no include option and its SWC plugin tries to disable the default transform, so SWC +// would also transform the workspace packages this project imports. SWC source maps place functions +// differently from the Oxc transform every other project uses, which makes merged coverage count each +// of those functions twice, with one copy never hit. SWC is only needed for the decorators in this app. const apiOnlySwcPlugin: Plugin = { ...swcPlugin, config: undefined, @@ -33,7 +33,7 @@ const apiOnlySwcPlugin: Plugin = { export default mergeConfig( baseConfig, defineProject({ - esbuild: { + oxc: { exclude: [/\.js$/, apiFilePattern] }, plugins: [apiOnlySwcPlugin, libnestPlugin], diff --git a/apps/gateway/vite.config.ts b/apps/gateway/vite.config.ts index 47aeafdc3..b860ad626 100644 --- a/apps/gateway/vite.config.ts +++ b/apps/gateway/vite.config.ts @@ -13,8 +13,10 @@ export default defineConfig(({ mode }) => ({ target: 'es2022' }, optimizeDeps: { - esbuildOptions: { - target: 'es2022' + rolldownOptions: { + transform: { + target: 'es2022' + } }, include: ['react/*', 'react-dom/*'] }, diff --git a/apps/playground/AGENTS.md b/apps/playground/AGENTS.md index 32fba6cec..ecf459ba2 100644 --- a/apps/playground/AGENTS.md +++ b/apps/playground/AGENTS.md @@ -14,10 +14,10 @@ must stay in exactly one module. `src/App.tsx` reaches it through `React.lazy`, the 13 MB `esbuild.wasm` download behind the Suspense fallback. Importing `IndexPage` eagerly, or adding a second `initialize()`, breaks the app at boot. -It depends on `build.target` and `optimizeDeps.esbuildOptions.target` being `es2022` in +It depends on `build.target` and `optimizeDeps.rolldownOptions.transform.target` being `es2022` in `vite.config.ts`. Lowering either one is how top-level await silently stops compiling. -`rollupOptions.external: ['esbuild']` is also required. `packages/instrument-bundler/src/vendor/esbuild.ts` +`build.rolldownOptions.external: ['esbuild']` is also required. `packages/instrument-bundler/src/vendor/esbuild.ts` picks between `esbuild` and `esbuild-wasm` on `typeof window`; without the external, Vite tries to pull the Node package into the browser bundle. Background: `.agents/docs/architecture/runtime-and-vendor.md`. @@ -59,7 +59,7 @@ degraded experience. `tsc` needs it too: the tsconfig maps `/runtime/v1/*` to `. which is the only reason example instruments importing `/runtime/v1/zod@3.x` type-check. `build.emptyOutDir` is `false` on purpose. The runtime plugin copies the runtime into -`dist/runtime/` from `buildStart`, and Vite empties the output directory _after_ the rollup +`dist/runtime/` from `buildStart`, and Vite empties the output directory _after_ the Rolldown build finishes. The cost is that `dist/` accumulates stale files — delete it by hand when a build looks wrong. diff --git a/apps/playground/vite.config.ts b/apps/playground/vite.config.ts index 51fd27d75..60c229308 100644 --- a/apps/playground/vite.config.ts +++ b/apps/playground/vite.config.ts @@ -7,7 +7,7 @@ import react from '@vitejs/plugin-react-swc'; import { defineConfig } from 'vite'; import type { Plugin } from 'vite'; -import type { PlaygroundConfig } from './src/preview/config'; +import type { PlaygroundConfig } from './src/preview/config.ts'; // Read straight from the monorepo root rather than through `@opendatacapture/release-info`: that // package pulls in `@opendatacapture/schemas`, which ships raw TypeScript, and loading it here would @@ -32,7 +32,7 @@ export default defineConfig(({ mode }) => ({ build: { chunkSizeWarningLimit: 1000, emptyOutDir: false, - rollupOptions: { + rolldownOptions: { external: ['esbuild'], input: { index: path.resolve(import.meta.dirname, 'index.html'), @@ -47,8 +47,10 @@ export default defineConfig(({ mode }) => ({ __GITHUB_REPO_URL__: `'${process.env.GITHUB_REPO_URL ?? '#'}'` }, optimizeDeps: { - esbuildOptions: { - target: 'es2022' + rolldownOptions: { + transform: { + target: 'es2022' + } }, exclude: ['@swc/wasm-web'], include: ['react/*', 'react-dom/*'] diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 8ab517d40..63fd1bbc4 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -26,7 +26,7 @@ export default defineConfig(async ({ command }) => { build: { chunkSizeWarningLimit: 1000, emptyOutDir: false, - rollupOptions: { + rolldownOptions: { external: ['esbuild'] }, sourcemap: true, @@ -36,8 +36,10 @@ export default defineConfig(async ({ command }) => { __RELEASE__: JSON.stringify(await getReleaseInfo()) }, optimizeDeps: { - esbuildOptions: { - target: 'es2022' + rolldownOptions: { + transform: { + target: 'es2022' + } }, include: ['react/*', 'react-dom/*'] }, diff --git a/packages/instrument-library/AGENTS.md b/packages/instrument-library/AGENTS.md index 17edbf92b..201e8aba9 100644 --- a/packages/instrument-library/AGENTS.md +++ b/packages/instrument-library/AGENTS.md @@ -84,7 +84,8 @@ grouped by kind; `--title` prints a flat list. **It reads `dist`, not `src`, so `packages/runtime-core/src` and every other `/runtime/v1/@[/]` to the `vendor/@/src/` wrapper it is built from — `tsconfig.json`'s `paths` to `runtime/v1/dist` applies only to `tsc`. A runtime import of a new vendored library resolves the - same way once its `vendor/` wrapper exists. + same way once its `vendor/` wrapper exists. That source needs runtime-core's `lowerDecorators` + plugin, which the config imports from `../runtime-core/vitest.config`. - `content.render(done)` of an interactive task mounts into `document.body`; JSX (a form `block`'s `render`) is rendered with `@testing-library/react`, a devDependency. CSS imports load as empty modules. diff --git a/packages/instrument-library/vitest.config.ts b/packages/instrument-library/vitest.config.ts index 6ce5c8e4b..010b4e550 100644 --- a/packages/instrument-library/vitest.config.ts +++ b/packages/instrument-library/vitest.config.ts @@ -3,6 +3,7 @@ import path from 'node:path'; import { defineProject, mergeConfig } from 'vitest/config'; import { baseConfig } from '../../vitest.config'; +import { lowerDecorators } from '../runtime-core/vitest.config'; const repositoryRoot = path.resolve(import.meta.dirname, '../..'); @@ -14,6 +15,7 @@ const PACKAGE_SPECIFIER = String.raw`((?:@[^/]+/)?[^/@]+@[^/]+)`; export default mergeConfig( baseConfig, defineProject({ + plugins: [lowerDecorators], resolve: { alias: [ { diff --git a/packages/runtime-core/AGENTS.md b/packages/runtime-core/AGENTS.md index a93b4b450..e06a271df 100644 --- a/packages/runtime-core/AGENTS.md +++ b/packages/runtime-core/AGENTS.md @@ -90,7 +90,10 @@ Two tiers, run by different tools. needed; relative imports carry `.js` like the rest of `src/` (Node16 resolution). `i18n.ts` picks `SynchronizedTranslator` or `StandaloneTranslator` from `window` **at import time**, so a test of a browser branch needs a `// @vitest-environment happy-dom` docblock, and one that varies `window` - needs `vi.resetModules()` and a dynamic `import('../i18n.js')`. + needs `vi.resetModules()` and a dynamic `import('../i18n.js')`. `vitest.config.ts` exports + `lowerDecorators`, which runs esbuild over source with decorators before Vite's Oxc transform: Oxc + cannot lower the TC39 decorators in `i18n.ts`, and Node cannot run them. Any project that runs + runtime-core source needs it in its `plugins`, as `packages/instrument-library` has. - **Types: `src/__tests__/define.test-d.ts` and `src/types/__tests__/instrument.form.test-d.ts`**, written with `expectTypeOf` from `expect-type` (a root devDependency). vitest does not collect them; the `tsc --noEmit` half of `pnpm --filter @opendatacapture/runtime-core lint` checks them, diff --git a/packages/runtime-core/vitest.config.ts b/packages/runtime-core/vitest.config.ts index ad645b017..6c1ce625a 100644 --- a/packages/runtime-core/vitest.config.ts +++ b/packages/runtime-core/vitest.config.ts @@ -1,10 +1,35 @@ +import * as path from 'node:path'; + +import { transform } from 'esbuild'; import { defineProject, mergeConfig } from 'vitest/config'; +import type { Plugin } from 'vitest/config'; import { baseConfig } from '../../vitest.config.js'; +const sourceRoot = path.join(import.meta.dirname, 'src') + path.sep; + +const decoratorPattern = /^\s*@[A-Za-z_$]/m; + +/** + * Oxc, which transforms TypeScript under Vite 8, cannot lower TC39 decorators, and Node cannot run + * them. esbuild, which builds `dist`, lowers them first in every project that runs this source. + */ +export const lowerDecorators: Plugin = { + enforce: 'pre', + name: 'lower-decorators', + async transform(code, id) { + if (!id.startsWith(sourceRoot) || !decoratorPattern.test(code)) { + return null; + } + const result = await transform(code, { loader: 'ts', sourcefile: id, sourcemap: true, target: 'es2022' }); + return { code: result.code, map: result.map }; + } +}; + export default mergeConfig( baseConfig, defineProject({ + plugins: [lowerDecorators], test: { name: 'runtime-core', root: import.meta.dirname diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1740464b3..64ae349c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -112,8 +112,8 @@ catalogs: specifier: 4.23.15 version: 4.23.15 vite: - specifier: ^7.3.6 - version: 7.3.6 + specifier: ^8.3.1 + version: 8.3.1 zustand: specifier: ^5.0.15 version: 5.0.15 @@ -204,7 +204,7 @@ importers: version: 6.0.3 vitest: specifier: ^5.0.2 - version: 5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) apps/api: dependencies: @@ -228,7 +228,7 @@ importers: version: 3.2.1(neverthrow@8.2.0)(zod@vendor+zod@3.x) '@douglasneuroinformatics/libnest': specifier: ^10.0.0 - version: 10.0.0(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-fastify@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@nestjs/testing@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3))(@swc/types@0.1.28)(fastify@5.12.5)(neverthrow@8.2.0)(reflect-metadata@0.2.2)(rollup@4.61.1)(rxjs@7.8.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))(vitest@5.0.2)(zod@vendor+zod@3.x) + version: 10.0.0(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-fastify@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@nestjs/testing@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3))(@swc/types@0.1.28)(fastify@5.12.5)(neverthrow@8.2.0)(reflect-metadata@0.2.2)(rollup@4.61.1)(rxjs@7.8.2)(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0))(vitest@5.0.2)(zod@vendor+zod@3.x) '@douglasneuroinformatics/libpasswd': specifier: 'catalog:' version: 0.0.3(typescript@6.0.3) @@ -422,7 +422,7 @@ importers: version: link:../../packages/vite-plugin-runtime '@tailwindcss/vite': specifier: 'catalog:' - version: 4.3.3(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 4.3.3(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@testing-library/dom': specifier: 'catalog:' version: 10.4.2 @@ -437,7 +437,7 @@ importers: version: 5.0.6 '@vitejs/plugin-react-swc': specifier: ^4.3.3 - version: 4.3.3(@swc/helpers@0.5.23)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 4.3.3(@swc/helpers@0.5.23)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) esbuild: specifier: 'catalog:' version: 0.28.2 @@ -458,7 +458,7 @@ importers: version: link:../../vendor/type-fest@4.x vite: specifier: 'catalog:' - version: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + version: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) apps/outreach: dependencies: @@ -501,10 +501,10 @@ importers: version: 3.7.4 '@astrojs/starlight': specifier: ^0.34.3 - version: 0.34.8(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)) + version: 0.34.8(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)) '@astrojs/starlight-tailwind': specifier: 4.0.2 - version: 4.0.2(@astrojs/starlight@0.34.8(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)))(tailwindcss@4.3.3) + version: 4.0.2(@astrojs/starlight@0.34.8(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)))(tailwindcss@4.3.3) '@opendatacapture/runtime-core': specifier: workspace:* version: link:../../packages/runtime-core @@ -516,10 +516,10 @@ importers: version: 0.5.20(tailwindcss@4.3.3) '@tailwindcss/vite': specifier: 'catalog:' - version: 4.3.3(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 4.3.3(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) astro: specifier: ^5.15.9 - version: 5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0) + version: 5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0) github-slugger: specifier: ^2.0.0 version: 2.0.0 @@ -631,10 +631,10 @@ importers: version: link:../../packages/vite-plugin-runtime '@storybook/react-vite': specifier: 'catalog:' - version: 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@tailwindcss/vite': specifier: 'catalog:' - version: 4.3.3(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 4.3.3(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@testing-library/dom': specifier: 'catalog:' version: 10.4.2 @@ -643,7 +643,7 @@ importers: version: 16.3.3(@testing-library/dom@10.4.2)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) '@vitejs/plugin-react-swc': specifier: ^4.3.3 - version: 4.3.3(@swc/helpers@0.5.23)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 4.3.3(@swc/helpers@0.5.23)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) happy-dom: specifier: 'catalog:' version: 20.11.15 @@ -655,7 +655,7 @@ importers: version: link:../../vendor/type-fest@4.x vite: specifier: 'catalog:' - version: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + version: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) apps/web: dependencies: @@ -788,13 +788,13 @@ importers: version: link:../../packages/vite-plugin-runtime '@storybook/react-vite': specifier: 'catalog:' - version: 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@tailwindcss/vite': specifier: 'catalog:' - version: 4.3.3(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 4.3.3(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@tanstack/router-plugin': specifier: ^1.168.41 - version: 1.168.41(@tanstack/react-router@1.170.40(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(esbuild@0.28.2)(rollup@4.61.1)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 1.168.41(@tanstack/react-router@1.170.40(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(esbuild@0.28.2)(rolldown@1.2.12)(rollup@4.61.1)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@testing-library/dom': specifier: 'catalog:' version: 10.4.2 @@ -806,7 +806,7 @@ importers: version: 1.5.6 '@vitejs/plugin-react-swc': specifier: ^4.3.3 - version: 4.3.3(@swc/helpers@0.5.23)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 4.3.3(@swc/helpers@0.5.23)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) happy-dom: specifier: 'catalog:' version: 20.11.15 @@ -815,10 +815,10 @@ importers: version: 4.3.3 vite: specifier: 'catalog:' - version: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + version: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) vite-plugin-compression: specifier: ^0.5.1 - version: 0.5.1(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 0.5.1(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) packages/demo: dependencies: @@ -1043,7 +1043,7 @@ importers: version: link:../instrument-stubs '@storybook/react-vite': specifier: 'catalog:' - version: 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@testing-library/dom': specifier: 'catalog:' version: 10.4.2 @@ -1058,7 +1058,7 @@ importers: version: link:../../vendor/type-fest@4.x vite: specifier: 'catalog:' - version: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + version: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) packages/release-info: dependencies: @@ -1229,7 +1229,7 @@ importers: version: link:../../runtime/v1 vite: specifier: 'catalog:' - version: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + version: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) runtime/v1: devDependencies: @@ -1395,10 +1395,10 @@ importers: version: 10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) '@storybook/react-vite': specifier: 'catalog:' - version: 10.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 10.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@tailwindcss/vite': specifier: 'catalog:' - version: 4.3.3(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + version: 4.3.3(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) esbuild-wasm: specifier: 'catalog:' version: 0.28.2 @@ -1407,7 +1407,7 @@ importers: version: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) vite: specifier: 'catalog:' - version: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + version: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) testing: dependencies: @@ -3739,6 +3739,9 @@ packages: '@oxc-project/types@0.150.0': resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==} + '@oxc-project/types@0.152.0': + resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} + '@oxc-resolver/binding-android-arm-eabi@11.21.2': resolution: {integrity: sha512-xQoCRv+gKax9KTdwdaQNnAFOai8neay7g3jExDIORzhbrejwGSJaZNTdOJHR5ziLg2joMxOCMOFMo4zuxza2uQ==} cpu: [arm] @@ -4914,6 +4917,102 @@ packages: react-redux: optional: true + '@rolldown/binding-android-arm-eabi@1.2.12': + resolution: {integrity: sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.12': + resolution: {integrity: sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.12': + resolution: {integrity: sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.12': + resolution: {integrity: sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.12': + resolution: {integrity: sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': + resolution: {integrity: sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.12': + resolution: {integrity: sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.12': + resolution: {integrity: sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.12': + resolution: {integrity: sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.12': + resolution: {integrity: sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.12': + resolution: {integrity: sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.12': + resolution: {integrity: sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.12': + resolution: {integrity: sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.12': + resolution: {integrity: sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.12': + resolution: {integrity: sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} @@ -8190,30 +8289,60 @@ packages: cpu: [arm64] os: [android] + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + lightningcss-darwin-arm64@1.32.0: resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + lightningcss-darwin-x64@1.32.0: resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + lightningcss-freebsd-x64@1.32.0: resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + lightningcss-linux-arm-gnueabihf@1.32.0: resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} @@ -8221,6 +8350,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} @@ -8228,6 +8364,13 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} @@ -8235,6 +8378,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} @@ -8242,22 +8392,45 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + lightningcss-win32-x64-msvc@1.32.0: resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + lightningcss@1.32.0: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -9631,6 +9804,11 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rolldown@1.2.12: + resolution: {integrity: sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup@4.61.1: resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -10605,15 +10783,16 @@ packages: yaml: optional: true - vite@7.3.6: - resolution: {integrity: sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==} + vite@8.3.1: + resolution: {integrity: sha512-/bvH9E9tmCXRGp2uXY3WbOldqpTwFkbha/8ANaEQ6VkxhH60KyqLwgZq6lG2y+4uT55x9+9eUHMpQ7uGnOCKjA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 + esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 - lightningcss: ^1.21.0 sass: ^1.70.0 sass-embedded: ^1.70.0 stylus: '>=0.54.8' @@ -10624,12 +10803,14 @@ packages: peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -11099,12 +11280,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/mdx@4.3.14(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0))': + '@astrojs/mdx@4.3.14(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0))': dependencies: '@astrojs/markdown-remark': 6.3.11 '@mdx-js/mdx': 3.1.1 acorn: 8.16.0 - astro: 5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0) + astro: 5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0) es-module-lexer: 1.7.0 estree-util-visit: 2.0.0 hast-util-to-html: 9.0.5 @@ -11127,22 +11308,22 @@ snapshots: sitemap: 9.0.1 zod: 4.4.3 - '@astrojs/starlight-tailwind@4.0.2(@astrojs/starlight@0.34.8(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)))(tailwindcss@4.3.3)': + '@astrojs/starlight-tailwind@4.0.2(@astrojs/starlight@0.34.8(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)))(tailwindcss@4.3.3)': dependencies: - '@astrojs/starlight': 0.34.8(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)) + '@astrojs/starlight': 0.34.8(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)) tailwindcss: 4.3.3 - '@astrojs/starlight@0.34.8(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0))': + '@astrojs/starlight@0.34.8(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0))': dependencies: '@astrojs/markdown-remark': 6.3.11 - '@astrojs/mdx': 4.3.14(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)) + '@astrojs/mdx': 4.3.14(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)) '@astrojs/sitemap': 3.7.4 '@pagefind/default-ui': 1.5.2 '@types/hast': 3.0.4 '@types/js-yaml': 4.0.9 '@types/mdast': 4.0.4 - astro: 5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0) - astro-expressive-code: 0.41.7(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)) + astro: 5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0) + astro-expressive-code: 0.41.7(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)) bcp-47: 2.1.0 hast-util-from-html: 2.0.3 hast-util-select: 6.0.4 @@ -11706,7 +11887,7 @@ snapshots: type-fest: 4.41.0 zod: link:vendor/zod@3.x - '@douglasneuroinformatics/libnest@10.0.0(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-fastify@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@nestjs/testing@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3))(@swc/types@0.1.28)(fastify@5.12.5)(neverthrow@8.2.0)(reflect-metadata@0.2.2)(rollup@4.61.1)(rxjs@7.8.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))(vitest@5.0.2)(zod@vendor+zod@3.x)': + '@douglasneuroinformatics/libnest@10.0.0(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-fastify@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@nestjs/testing@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@12.1.1(@nestjs/common@12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)))(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3))(@swc/types@0.1.28)(fastify@5.12.5)(neverthrow@8.2.0)(reflect-metadata@0.2.2)(rollup@4.61.1)(rxjs@7.8.2)(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0))(vitest@5.0.2)(zod@vendor+zod@3.x)': dependencies: '@douglasneuroinformatics/libjs': 3.2.1(neverthrow@8.2.0)(zod@vendor+zod@3.x) '@nestjs/common': 12.1.1(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -11736,10 +11917,10 @@ snapshots: ts-morph: 27.0.2 type-fest: 5.7.0 unplugin-swc: 1.5.9(@swc/core@1.16.2(@swc/helpers@0.5.23))(rollup@4.61.1) - vitest: 5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + vitest: 5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) zod: link:vendor/zod@3.x optionalDependencies: - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) transitivePeerDependencies: - '@swc/types' - rollup @@ -12526,11 +12707,11 @@ snapshots: '@isaacs/cliui@9.0.0': {} - '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: glob: 13.0.6 react-docgen-typescript: 2.4.0(typescript@6.0.3) - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) optionalDependencies: typescript: 6.0.3 @@ -13015,6 +13196,8 @@ snapshots: '@oxc-project/types@0.150.0': {} + '@oxc-project/types@0.152.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.21.2': optional: true @@ -14381,6 +14564,51 @@ snapshots: react: link:vendor/react@19.x react-redux: 9.3.0(react@vendor+react@19.x)(redux@5.0.1) + '@rolldown/binding-android-arm-eabi@1.2.12': + optional: true + + '@rolldown/binding-android-arm64@1.2.12': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.12': + optional: true + + '@rolldown/binding-darwin-x64@1.2.12': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.12': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.12': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.12': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.12': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.12': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.12': + optional: true + '@rolldown/pluginutils@1.0.1': {} '@rollup/pluginutils@5.4.0(rollup@4.61.1)': @@ -14669,17 +14897,17 @@ snapshots: storybook: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) ts-dedent: 2.2.0 - '@storybook/builder-vite@10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@storybook/builder-vite@10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: storybook: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) ts-dedent: 2.2.0 - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) - '@storybook/builder-vite@10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@storybook/builder-vite@10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: storybook: 10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) ts-dedent: 2.2.0 - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) '@storybook/global@5.0.0': {} @@ -14716,11 +14944,11 @@ snapshots: react-dom: link:vendor/react-dom@19.x storybook: 10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) - '@storybook/react-vite@10.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@storybook/react-vite@10.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@rollup/pluginutils': 5.4.0(rollup@4.61.1) - '@storybook/builder-vite': 10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + '@storybook/builder-vite': 10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@storybook/react': 10.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@6.0.3) empathic: 2.0.1 magic-string: 1.4.2 @@ -14730,7 +14958,7 @@ snapshots: resolve: 1.22.12 storybook: 10.6.1(prettier@3.9.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) tsconfig-paths: 4.2.0 - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) optionalDependencies: typescript: 6.0.3 transitivePeerDependencies: @@ -14739,11 +14967,11 @@ snapshots: - rollup - supports-color - '@storybook/react-vite@10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@storybook/react-vite@10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(rollup@4.61.1)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@rollup/pluginutils': 5.4.0(rollup@4.61.1) - '@storybook/builder-vite': 10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + '@storybook/builder-vite': 10.6.1(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@storybook/react': 10.6.1(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x)(storybook@10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(typescript@6.0.3) empathic: 2.0.1 magic-string: 1.4.2 @@ -14753,7 +14981,7 @@ snapshots: resolve: 1.22.12 storybook: 10.6.1(prettier@3.9.9)(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) tsconfig-paths: 4.2.0 - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) optionalDependencies: typescript: 6.0.3 transitivePeerDependencies: @@ -14956,12 +15184,12 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 4.3.3 - '@tailwindcss/vite@4.3.3(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.3(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.3 '@tailwindcss/oxide': 4.3.3 tailwindcss: 4.3.3 - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) '@tanstack/history@1.162.4': {} @@ -15067,7 +15295,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.168.41(@tanstack/react-router@1.170.40(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(esbuild@0.28.2)(rollup@4.61.1)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@tanstack/router-plugin@1.168.41(@tanstack/react-router@1.170.40(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x))(esbuild@0.28.2)(rolldown@1.2.12)(rollup@4.61.1)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.7 '@babel/template': 7.29.7 @@ -15076,11 +15304,11 @@ snapshots: '@tanstack/router-generator': 1.167.39 '@tanstack/router-utils': 1.162.3 chokidar: 5.0.0 - unplugin: 3.4.0(esbuild@0.28.2)(rollup@4.61.1)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + unplugin: 3.4.0(esbuild@0.28.2)(rolldown@1.2.12)(rollup@4.61.1)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) zod: 4.6.5 optionalDependencies: '@tanstack/react-router': 1.170.40(react-dom@vendor+react-dom@19.x)(react@vendor+react@19.x) - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) transitivePeerDependencies: - '@farmfe/core' - '@rspack/core' @@ -15494,11 +15722,11 @@ snapshots: '@ungap/structured-clone@1.3.1': {} - '@vitejs/plugin-react-swc@4.3.3(@swc/helpers@0.5.23)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@vitejs/plugin-react-swc@4.3.3(@swc/helpers@0.5.23)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: '@rolldown/pluginutils': 1.0.1 '@swc/core': 1.16.2(@swc/helpers@0.5.23) - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) transitivePeerDependencies: - '@swc/helpers' @@ -15512,7 +15740,7 @@ snapshots: obug: 2.2.1 std-env: 4.3.0 tinyrainbow: 3.1.1 - vitest: 5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + vitest: 5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) '@vitest/expect@3.2.4': dependencies: @@ -15528,14 +15756,14 @@ snapshots: dependencies: '@vitest/istanbul-lib-coverage': 1.0.2 - '@vitest/mocker@5.0.2(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0))': + '@vitest/mocker@5.0.2(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: '@jridgewell/trace-mapping': 0.3.31 '@vitest/spy': 5.0.2 estree-walker: 3.0.3 magic-string: 1.4.2 optionalDependencies: - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) '@vitest/pretty-format@3.2.4': dependencies: @@ -15816,12 +16044,12 @@ snapshots: transitivePeerDependencies: - supports-color - astro-expressive-code@0.41.7(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)): + astro-expressive-code@0.41.7(astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0)): dependencies: - astro: 5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0) + astro: 5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0) rehype-expressive-code: 0.41.7 - astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0): + astro@5.18.2(@types/node@24.19.0)(idb-keyval@6.3.0)(jiti@2.7.0)(lightningcss@1.33.0)(rollup@4.61.1)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.0): dependencies: '@astrojs/compiler': 2.13.1 '@astrojs/internal-helpers': 0.7.6 @@ -15878,8 +16106,8 @@ snapshots: unist-util-visit: 5.1.0 unstorage: 1.17.5(idb-keyval@6.3.0) vfile: 6.0.3 - vite: 6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) - vitefu: 1.1.3(vite@6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + vite: 6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.33.0)(tsx@4.23.15)(yaml@2.9.0) + vitefu: 1.1.3(vite@6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.33.0)(tsx@4.23.15)(yaml@2.9.0)) xxhash-wasm: 1.1.0 yargs-parser: 21.1.1 yocto-spinner: 0.2.3 @@ -17290,10 +17518,6 @@ snapshots: dependencies: walk-up-path: 4.0.0 - fdir@6.5.0(picomatch@4.0.4): - optionalDependencies: - picomatch: 4.0.4 - fdir@6.5.0(picomatch@4.0.7): optionalDependencies: picomatch: 4.0.7 @@ -18232,36 +18456,69 @@ snapshots: lightningcss-android-arm64@1.32.0: optional: true + lightningcss-android-arm64@1.33.0: + optional: true + lightningcss-darwin-arm64@1.32.0: optional: true + lightningcss-darwin-arm64@1.33.0: + optional: true + lightningcss-darwin-x64@1.32.0: optional: true + lightningcss-darwin-x64@1.33.0: + optional: true + lightningcss-freebsd-x64@1.32.0: optional: true + lightningcss-freebsd-x64@1.33.0: + optional: true + lightningcss-linux-arm-gnueabihf@1.32.0: optional: true + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + lightningcss-linux-arm64-gnu@1.32.0: optional: true + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + lightningcss-linux-arm64-musl@1.32.0: optional: true + lightningcss-linux-arm64-musl@1.33.0: + optional: true + lightningcss-linux-x64-gnu@1.32.0: optional: true + lightningcss-linux-x64-gnu@1.33.0: + optional: true + lightningcss-linux-x64-musl@1.32.0: optional: true + lightningcss-linux-x64-musl@1.33.0: + optional: true + lightningcss-win32-arm64-msvc@1.32.0: optional: true + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + lightningcss-win32-x64-msvc@1.32.0: optional: true + lightningcss-win32-x64-msvc@1.33.0: + optional: true + lightningcss@1.32.0: dependencies: detect-libc: 2.1.2 @@ -18278,6 +18535,22 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + lilconfig@2.1.0: {} lines-and-columns@1.2.4: {} @@ -20050,6 +20323,27 @@ snapshots: rfdc@1.4.1: {} + rolldown@1.2.12: + dependencies: + '@oxc-project/types': 0.152.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.12 + '@rolldown/binding-android-arm64': 1.2.12 + '@rolldown/binding-darwin-arm64': 1.2.12 + '@rolldown/binding-darwin-x64': 1.2.12 + '@rolldown/binding-freebsd-x64': 1.2.12 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.12 + '@rolldown/binding-linux-arm64-gnu': 1.2.12 + '@rolldown/binding-linux-arm64-musl': 1.2.12 + '@rolldown/binding-linux-ppc64-gnu': 1.2.12 + '@rolldown/binding-linux-s390x-gnu': 1.2.12 + '@rolldown/binding-linux-x64-gnu': 1.2.12 + '@rolldown/binding-linux-x64-musl': 1.2.12 + '@rolldown/binding-openharmony-arm64': 1.2.12 + '@rolldown/binding-win32-arm64-msvc': 1.2.12 + '@rolldown/binding-win32-x64-msvc': 1.2.12 + rollup@4.61.1: dependencies: '@types/estree': 1.0.9 @@ -21005,15 +21299,16 @@ snapshots: picomatch: 4.0.7 webpack-virtual-modules: 0.6.2 - unplugin@3.4.0(esbuild@0.28.2)(rollup@4.61.1)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)): + unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.12)(rollup@4.61.1)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)): dependencies: '@jridgewell/remapping': 2.3.5 picomatch: 4.0.7 webpack-virtual-modules: 0.6.2 optionalDependencies: esbuild: 0.28.2 + rolldown: 1.2.12 rollup: 4.61.1 - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) unstorage@1.17.5(idb-keyval@6.3.0): dependencies: @@ -21128,16 +21423,16 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-plugin-compression@0.5.1(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)): + vite-plugin-compression@0.5.1(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)): dependencies: chalk: 4.1.2 debug: 4.4.3 fs-extra: 10.1.0 - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) transitivePeerDependencies: - supports-color - vite@6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0): + vite@6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.33.0)(tsx@4.23.15)(yaml@2.9.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.7) @@ -21149,34 +21444,33 @@ snapshots: '@types/node': 24.19.0 fsevents: 2.3.3 jiti: 2.7.0 - lightningcss: 1.32.0 + lightningcss: 1.33.0 tsx: 4.23.15 yaml: 2.9.0 - vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0): + vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0): dependencies: - esbuild: 0.28.2 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + lightningcss: 1.33.0 + picomatch: 4.0.7 postcss: 8.5.28 - rollup: 4.61.1 + rolldown: 1.2.12 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 24.19.0 + esbuild: 0.28.2 fsevents: 2.3.3 jiti: 2.7.0 - lightningcss: 1.32.0 tsx: 4.23.15 yaml: 2.9.0 - vitefu@1.1.3(vite@6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)): + vitefu@1.1.3(vite@6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.33.0)(tsx@4.23.15)(yaml@2.9.0)): optionalDependencies: - vite: 6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 6.4.3(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.33.0)(tsx@4.23.15)(yaml@2.9.0) - vitest@5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)): + vitest@5.0.2(@types/node@24.19.0)(@vitest/coverage-v8@5.0.2)(happy-dom@20.11.15)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.2(vite@7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0)) + '@vitest/mocker': 5.0.2(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0)) chai: 6.2.2 es-module-lexer: 2.3.2 expect-type: 1.4.0 @@ -21187,7 +21481,7 @@ snapshots: tinybench: 6.2.0 tinyexec: 1.3.1 tinyglobby: 0.2.17 - vite: 7.3.6(@types/node@24.19.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.0) + vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.0) why-is-node-running: 3.2.2 optionalDependencies: '@types/node': 24.19.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 08171da0c..407583fd7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -44,7 +44,7 @@ catalog: tailwindcss: '4.3.3' tsx: '4.23.15' serialize-error: '^13.0.1' - vite: '^7.3.6' + vite: '^8.3.1' zustand: ^5.0.15 '@storybook/addon-docs': '^10.6.1' '@storybook/addon-themes': '^10.6.1' diff --git a/storybook/AGENTS.md b/storybook/AGENTS.md index 720e8225f..9464947ce 100644 --- a/storybook/AGENTS.md +++ b/storybook/AGENTS.md @@ -41,9 +41,9 @@ pattern but reports "Already up to date" and leaves the link in place. Delete th ## The `@` alias resolves by importer, and only for two apps -`vite.config.js` replaces the usual static alias with a `customResolver` that maps `@` to -`apps/playground/src` or `apps/web/src` depending on which file did the importing, and returns -`null` for everything else. **A `packages/react-core` story that imports `@/…` will fail to +`vite.config.js` replaces the usual static alias with a `source-root-alias` plugin (`enforce: 'pre'`) +that maps `@` to `apps/playground/src` or `apps/web/src` depending on which file did the importing, +and returns `null` for everything else. **A `packages/react-core` story that imports `@/…` will fail to resolve** — react-core has no `@` alias of its own, so import relatively or by package name there. ## Tailwind scans only the directories named in `config/globals.css` diff --git a/storybook/vite.config.js b/storybook/vite.config.js index b28e98392..5bd9728c9 100644 --- a/storybook/vite.config.js +++ b/storybook/vite.config.js @@ -7,35 +7,38 @@ import { defineConfig } from 'vite'; const playgroundSourceRoot = path.resolve(import.meta.dirname, '../apps/playground/src'); const webSourceRoot = path.resolve(import.meta.dirname, '../apps/web/src'); +/** @type {import('vite').Plugin} */ +const sourceRootAlias = { + enforce: 'pre', + name: 'source-root-alias', + resolveId(source, importer, options) { + if (source !== '@' && !source.startsWith('@/')) { + return null; + } + const sourceRoot = [playgroundSourceRoot, webSourceRoot].find((root) => importer?.startsWith(root)); + if (!sourceRoot) { + return null; + } + return this.resolve(sourceRoot + source.slice(1), importer, options); + } +}; + export default defineConfig({ build: { target: 'es2022' }, optimizeDeps: { - esbuildOptions: { - target: 'es2022' + rolldownOptions: { + transform: { + target: 'es2022' + } } }, plugins: [ + sourceRootAlias, runtime({ rootDir: import.meta.dirname }), tailwindcss() - ], - resolve: { - alias: [ - { - customResolver: async function (source, importer) { - if (importer?.startsWith(playgroundSourceRoot)) { - return this.resolve(source.replace('{{ROOT}}', playgroundSourceRoot)); - } else if (importer?.startsWith(webSourceRoot)) { - return this.resolve(source.replace('{{ROOT}}', webSourceRoot)); - } - return null; - }, - find: '@', - replacement: '{{ROOT}}' - } - ] - } + ] });