From 145472acf3b355d3f147ec5db3380fb974c9bb37 Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Tue, 1 Sep 2026 16:24:47 -0700 Subject: [PATCH 1/6] chore: initial storybook scaffolding --- .gitignore | 3 + .storybook/main.ts | 9 + .storybook/preview.tsx | 14 + eslint.config.mjs | 4 + package.json | 10 +- pnpm-lock.yaml | 1831 +++++++++++++++++++++++++++++++------ stories/Button.stories.ts | 54 ++ stories/Button.tsx | 43 + stories/button.css | 30 + vitest.config.mts | 23 +- vitest.shims.d.ts | 1 + 11 files changed, 1736 insertions(+), 286 deletions(-) create mode 100644 .storybook/main.ts create mode 100644 .storybook/preview.tsx create mode 100644 stories/Button.stories.ts create mode 100644 stories/Button.tsx create mode 100644 stories/button.css create mode 100644 vitest.shims.d.ts diff --git a/.gitignore b/.gitignore index f5e550b..453e44a 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ next-env.d.ts # IDEs .vscode/ .idea/ + +*storybook.log +storybook-static diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 0000000..2617af8 --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,9 @@ +import type { StorybookConfig } from "@storybook/nextjs-vite"; + +const config: StorybookConfig = { + stories: ["../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)"], + addons: ["@storybook/addon-mcp"], + framework: "@storybook/nextjs-vite", + staticDirs: ["../public"], +}; +export default config; diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx new file mode 100644 index 0000000..c0adadc --- /dev/null +++ b/.storybook/preview.tsx @@ -0,0 +1,14 @@ +import type { Preview } from "@storybook/nextjs-vite"; + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, +}; + +export default preview; diff --git a/eslint.config.mjs b/eslint.config.mjs index eedcf90..ce98339 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,3 +1,6 @@ +// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format +import storybook from "eslint-plugin-storybook"; + import { defineConfig, globalIgnores } from "eslint/config"; import eslintConfigPrettier from "eslint-config-prettier/flat"; import nextVitals from "eslint-config-next/core-web-vitals"; @@ -14,4 +17,5 @@ export default defineConfig([ "next-env.d.ts", ]), eslintConfigPrettier, + ...storybook.configs["flat/recommended"], ]); diff --git a/package.json b/package.json index 6f76ed4..ffdc44a 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,9 @@ "db:migrate": "netlify database migrations apply", "db:status": "netlify database status", "auth:generate": "BETTER_AUTH_SCHEMA_GENERATION=1 auth generate --config ./lib/auth-config.ts --output ./db/auth-schema.ts --yes", - "auth:create-admin": "auth create-admin --config ./lib/auth-config.ts" + "auth:create-admin": "auth create-admin --config ./lib/auth-config.ts", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" }, "dependencies": { "@better-auth/drizzle-adapter": "1.7.1", @@ -73,6 +75,10 @@ "prettier": "3.9.6", "typescript": "^6.0.3", "vite": "^8.2.1", - "vitest": "5.0.0-beta.7" + "vitest": "5.0.0-beta.7", + "storybook": "^10.5.10", + "@storybook/nextjs-vite": "^10.5.10", + "@storybook/addon-mcp": "^0.7.0", + "eslint-plugin-storybook": "^10.5.10" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eaefde6..2b286a2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,9 +5,7 @@ settings: excludeLinksFromLockfile: false patchedDependencies: - drizzle-orm@1.0.0-rc.4: - hash: 4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867 - path: patches/drizzle-orm@1.0.0-rc.4.patch + drizzle-orm@1.0.0-rc.4: 4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867 importers: @@ -15,7 +13,7 @@ importers: dependencies: '@better-auth/drizzle-adapter': specifier: 1.7.1 - version: 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3)) + version: 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3)) '@lexical/code': specifier: 0.49.0 version: 0.49.0(typescript@6.0.3) @@ -48,19 +46,19 @@ importers: version: 2.1.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) better-auth: specifier: 1.7.1 - version: 1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-beta.7) + version: 1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-beta.7) dayjs: specifier: ^1.11.23 version: 1.11.23 drizzle-orm: specifier: 1.0.0-rc.4 - version: 1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3) + version: 1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3) lexical: specifier: 0.49.0 version: 0.49.0(typescript@6.0.3) next: specifier: ^16.3.1 - version: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: specifier: ^19.1.0 version: 19.2.8 @@ -69,13 +67,13 @@ importers: version: 19.2.8(react@19.2.8) react-email: specifier: ^6.9.2 - version: 6.9.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 6.9.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2) react-markdown: specifier: 10.1.0 - version: 10.1.0(@types/react@19.2.18)(react@19.2.8) + version: 10.1.0(@types/react@19.2.18)(react@19.2.8)(supports-color@10.2.2) remark-gfm: specifier: 4.0.1 - version: 4.0.1 + version: 4.0.1(supports-color@10.2.2) resend: specifier: ^6.20.0 version: 6.20.0(@react-email/render@2.1.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) @@ -91,7 +89,13 @@ importers: version: 1.0.0 '@react-email/ui': specifier: 6.9.2 - version: 6.9.2(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 6.9.2(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@storybook/addon-mcp': + specifier: ^0.7.0 + version: 0.7.0(@storybook/addon-vitest@10.5.10(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/browser@5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-beta.7))(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(typescript@6.0.3) + '@storybook/nextjs-vite': + specifier: ^10.5.10 + version: 10.5.10(@babel/core@7.29.7(supports-color@10.2.2))(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.1)(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(supports-color@10.2.2)(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) '@testing-library/dom': specifier: ^10.4.1 version: 10.4.1 @@ -121,28 +125,34 @@ importers: version: 5.0.0-beta.7(playwright@1.62.1)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7) auth: specifier: ^1.7.1 - version: 1.7.1(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(chokidar@5.0.0)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1)(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-beta.7) + version: 1.7.1(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(chokidar@5.0.0)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1)(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2)(vitest@5.0.0-beta.7) drizzle-kit: specifier: 1.0.0-rc.4 version: 1.0.0-rc.4 eslint: specifier: ^9.31.0 - version: 9.39.5(jiti@2.7.0) + version: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) eslint-config-next: specifier: ^16.3.1 - version: 16.3.1(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + version: 16.3.1(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) eslint-config-prettier: specifier: 10.1.8 - version: 10.1.8(eslint@9.39.5(jiti@2.7.0)) + version: 10.1.8(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-storybook: + specifier: ^10.5.10 + version: 10.5.10(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) netlify-cli: specifier: ^27.1.2 - version: 27.1.2(@types/node@24.13.3)(picomatch@4.0.5) + version: 27.1.2(@types/node@24.13.3)(picomatch@4.0.5)(supports-color@10.2.2) playwright: specifier: 1.62.1 version: 1.62.1 prettier: specifier: 3.9.6 version: 3.9.6 + storybook: + specifier: ^10.5.10 + version: 10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8) typescript: specifier: ^6.0.3 version: 6.0.3 @@ -528,15 +538,30 @@ packages: '@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.9.2': + resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==} + '@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.3': resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@emnapi/runtime@1.9.2': + resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} + '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@envelop/instrumentation@1.0.0': resolution: {integrity: sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw==} engines: {node: '>=18.0.0'} @@ -1312,6 +1337,15 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} + '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0': + resolution: {integrity: sha512-qvsTEwEFefhdirGOPnu9Wp6ChfIwy2dBCRuETU3uE+4cC+PFoxMSiiEhxk4lOluA34eARHA0OxqsEUYDqRMgeQ==} + peerDependencies: + typescript: '>= 4.3.x' + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + typescript: + optional: true + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1819,6 +1853,9 @@ packages: '@next/env@16.3.0': resolution: {integrity: sha512-o9r1S0BNiNreHP9Vs+Qnqd9kviDkJh8xIACY7UFZSmiGbbQRzPBBosvHzAU4TULHOIuOj/18RSsyz2qrREmIFw==} + '@next/env@16.3.0-preview.5': + resolution: {integrity: sha512-XqdVR0utAWMsVc1OIyO48D32vrdmC4/uAgI3Ds088YlOO4vfGKXXVyvkGFkOZkOK0xg7bNYNfJAarX4A0tYqGg==} + '@next/env@16.3.1': resolution: {integrity: sha512-35G3xwkQUb2oETSDjFXGrVugknoayLFBh7vSE+yAcl9IP2zT9wyGwq7297AYHR11kJld807t5f8AJBs6WBzXsQ==} @@ -2075,9 +2112,242 @@ packages: resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} engines: {node: '>=14'} + '@oxc-parser/binding-android-arm-eabi@0.127.0': + resolution: {integrity: sha512-0LC7ye4hvqbIKxAzThzvswgHLFu2AURKzYLeSVvLdu2TBOYWQDmHnTqPLeA597BcUCxiLqLsS4CJ5uoI5WYWCQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.127.0': + resolution: {integrity: sha512-b5jtVTH6AU5CJXHNdj7Jj9IEiR9yVjjnwHzPJhGyHGPdcsZSzBCkS9GBbV33niRMvKthDwQRFRJfI4a+k4PvYg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.127.0': + resolution: {integrity: sha512-obCE8B7ISKkJidjlhv9xRGJPOSDG2Yu6PRga9Ruaz35uintHxbp1Ki/Yc71wx4rj3Edrm0a1kzG1TAwit0wFpg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.127.0': + resolution: {integrity: sha512-JL6Xb5IwPQT8rUzlpsX7E+AgfcdNklXNPFp8pjCQQ5MQOQo5rtEB2ui+3Hgg9Sn7Y9Egj6YOLLiHhLpdAe12Aw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.127.0': + resolution: {integrity: sha512-SDQ/3MQFw58fqQz3Z1PhSKFF3JoCF4gmlNjziDm8X02tTahCw0qJbd7FGPDKw1i4VTBZene9JPyC3mHtSvi+wA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.127.0': + resolution: {integrity: sha512-Av+D1MIqzV0YMGPT9we2SIZaMKD7Cxs4CvXSx/yxaWHewZjYEjScpOf5igc8IILASViw4WTnjlwUdI1KzVtDHQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.127.0': + resolution: {integrity: sha512-Cs2fdJ8cPpFdeebj6p4dag8A4+56hPvZ0AhQQzlaLswGz1tz7bXt1nETLeorrM9+AMcWFFkqxcXwDGfTVidY8g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.127.0': + resolution: {integrity: sha512-qdOfTcT6SY8gsJrrV92uyEUyjqMGPpIB5JZUG6QN5dukYd+7/j0kX6MwK1DgQj39jtUYixxPiaRUiEN1+0CXgQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-arm64-musl@0.127.0': + resolution: {integrity: sha512-EoTCZneNFU/P2qrpEM+RHmQwt+CvDkyGESG6qhr7KaegXLZwePfbrkCDfAk8/rhxbDUVGsZILX+2tqPzFtoFWA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-ppc64-gnu@0.127.0': + resolution: {integrity: sha512-zALjmZYgxFLHjXeudcDF0xFGNydTAtkAeXAr2EuC17ywCyFxcmQra4w0BMde0Yi/re4Bi4iwEoEXtYN7l6eBLQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-gnu@0.127.0': + resolution: {integrity: sha512-fPP8M6zQLS7Jz7o9d5ArUSuAuSK3e+WCYVrCpdzeCOejidtZExJ9tjhDrAd3HEPqARBCPmdpqxESPFqy44vkBQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-musl@0.127.0': + resolution: {integrity: sha512-7IcC4Ao02oGpfnjt+X/oF4U2mllo2qoSkw5xxiXNKL9MCTsTiAC6616beOuehdxGcnz1bRoPC1RQ2f1GQDdN+g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-s390x-gnu@0.127.0': + resolution: {integrity: sha512-pbXIhiNFHoqWeqDNLiJ9JkpHz1IM9k4DXa66x+1GTWMG7iLxtkXgE53iiuKSXwmk3zIYmaPVfBvgcAhS583K4Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-gnu@0.127.0': + resolution: {integrity: sha512-MYCguB9RvBvlSd6gbuNI7QwiLoCCAlGnlRJFPrzLI6U1/9wkC/WK6LtBAUln55H1Ctqw45PWmqrobKoMhsYQzQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-musl@0.127.0': + resolution: {integrity: sha512-5eY0B/bxf1xIUxb4NOTvOI3KWtBQfPWYyKAzgcrCt0mDibSZygVpO1Pz8bkeiSZ5Jj9+M09dkggG3H8I5d0Uyg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-openharmony-arm64@0.127.0': + resolution: {integrity: sha512-Gld0ajrFTUXNtdw20fVBuTQx66FA75nIVg+//pPfR3sXkuABB4mTBhl3r9JNzrJpgW//qiwxf0nWXUWGJSL3UQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.127.0': + resolution: {integrity: sha512-T6KVD7rhLzFlwGRXMnxUFfkCZD8FHnb968wVXW1mXzgRFc5RNXOBY2mPPDZ77x5Ln76ltLMgtPg0cOkU1NSrEQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.127.0': + resolution: {integrity: sha512-Ujvw4X+LD1CCGULcsQcvb4YNVoBGqt+JHgNNzGGaCImELiZLk477ifUH53gIbE7EKd933NdTi25JWEr9K2HwXw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.127.0': + resolution: {integrity: sha512-0cwxKO7KHQQQfo4Uf4B2SQrhgm+cJaP9OvFFhx52Tkg4bezsacu83GB2/In5bC415Ueeym+kXdnge/57rbSfTw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.127.0': + resolution: {integrity: sha512-rOrnSQSCbhI2kowr9XxE7m9a8oQXnBHjnS6j95LxxAnEZ0+Fz20WlRXG4ondQb+ejjt2KOsa65sE6++L6kUd+w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.127.0': + resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} + '@oxc-project/types@0.144.0': resolution: {integrity: sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==} + '@oxc-resolver/binding-android-arm-eabi@11.21.2': + resolution: {integrity: sha512-xQoCRv+gKax9KTdwdaQNnAFOai8neay7g3jExDIORzhbrejwGSJaZNTdOJHR5ziLg2joMxOCMOFMo4zuxza2uQ==} + cpu: [arm] + os: [android] + + '@oxc-resolver/binding-android-arm64@11.21.2': + resolution: {integrity: sha512-HF5oiE2L05yInPYCFD/4uxSrEZW4SuIfn99Y6L1xnJnzl066JR+MJs2rIdstw8A2MPlAKH+13dpFPNycjqzvGg==} + cpu: [arm64] + os: [android] + + '@oxc-resolver/binding-darwin-arm64@11.21.2': + resolution: {integrity: sha512-UX4u49CVCAD8QZNELaW8eMGgMAGwFWYEPbvNsh+3r/gs4NX3KfpiACMVwRQT0EuH3uat9hM5Zl+Ppm9pJD8tgg==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@11.21.2': + resolution: {integrity: sha512-J9xPx7YBkrRmJ+xl561ztnMWEc1aOyjEIxBiGX1dVb3u7bGSnfObfcZk+Pd+uM0HZAPNsQ1xvD8j52A/uOSqNQ==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@11.21.2': + resolution: {integrity: sha512-fRlt7OvSaQkWj6+EDTVxawVxOlqJB2QSnBfkeCyK4RTvsGctbw3BiH2Tb7DzMs7bikc4BRBpvWP5zF9K8b54Zg==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.21.2': + resolution: {integrity: sha512-gK+vPUcPQITkGwBKpZGrcDHSlU6eDGl7AQacxS2CEKAZIBHWkOVFeJwLZ4tYnA1acJqRM5lt7yYwPCVqGHIJ7A==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm-musleabihf@11.21.2': + resolution: {integrity: sha512-L/Rgas7SrOKy/z7IH+HxSFRqVO4PuLDKLEGKvnhoCBBq3UJ0YzGBou3qMzaAGgkJwsIvX2pBF+7ojzfUhLiZxQ==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@11.21.2': + resolution: {integrity: sha512-CUEYvlX1Fk7E9kUMzuswru1J9HLxMwnpDeQGjQuI4ZH+iNCoa2X9T+pvyzrbsgl7WnIeTFTlNlHsRfVdf5g9/g==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-arm64-musl@11.21.2': + resolution: {integrity: sha512-ViN1ZibQyxwC67GpoP33oo+S9UyUnkog13vzQb9+v9bCNvrVzJuk0MRdacjtv/9xfRMVF/eqHFyl8YOeykI10Q==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-linux-ppc64-gnu@11.21.2': + resolution: {integrity: sha512-CU1sCqWnhGqYD5I1HedHk5pujrn7ssDkNB/AEQd/pd3D/EojVSgJUlpbafwIbyhia3PgIfkvdFpRPWSALzVumA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-riscv64-gnu@11.21.2': + resolution: {integrity: sha512-jWVyZtIHca4Gb96x7dag+y69vlei7ffjrsveLkmf2ZhqEAz6ZSBnY1GWvgZUaZlwZP62A3xD092BW97Q5VGc+g==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-riscv64-musl@11.21.2': + resolution: {integrity: sha512-LF29obFqNgBUgDX7rmUK7M4D0JQG5LxhYzn3xXmECcHU9aQAdWG7NiY052qybtesEdwHQXKNTWYQ7mTsybNvWg==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-linux-s390x-gnu@11.21.2': + resolution: {integrity: sha512-+NYcm+cCHBbtdQQ3A4phQTSuVRYnNHz7wrl9XRAPEovcdoqi0mb1K5ZOl+jN54ZD+q1zz3V0vltbFJmzecJKmw==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-x64-gnu@11.21.2': + resolution: {integrity: sha512-UQqZDdG2r2HhAOsZEgufkIWHPQ886IUyuJQkoZByvzhW8j51R4UNzGBJFkTiTnLhQnggwJRdJgFWK4uY6ZVIMw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-x64-musl@11.21.2': + resolution: {integrity: sha512-3Q9PMRjalWkT6NZ4jfujuqTCFwoWErg3y3BnOgb544B8IMw4PktiwWOigMfOHNRLMghZeJ7hpfpZf4CP7rV7Og==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-openharmony-arm64@11.21.2': + resolution: {integrity: sha512-Eljeq3ndtyKhM+Es8LITi4Zl2htzuRZcrPMF3kMCsrILztvU6AjZ3FuEhHHKobPUB9rMpzLpJP5bDqXI7r+iog==} + cpu: [arm64] + os: [openharmony] + + '@oxc-resolver/binding-wasm32-wasi@11.21.2': + resolution: {integrity: sha512-HGDbNsIywqc4LxU38+CTJNnB/6BF7rheWOJ259b4eE0aEnelYblC8x+1tEd63bp31fYne63RQz9Jb4yVnC7Yig==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@11.21.2': + resolution: {integrity: sha512-iWx25CBEgH49iE9q5coEGI/jb1jl5kkCY9z6U5Og67xCkQ/WFMDc2J5U78+AE91SUxM2NqSLhJC8/PLfWnImww==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@11.21.2': + resolution: {integrity: sha512-VPoCAhKvCQTlG7vxqaBXcmuvbh77BfnGXj8g0pbvVXpm1F/R8rDVwqIWcEbMzrI1JvJlm8v7T9uMVQb6UctMRg==} + cpu: [x64] + os: [win32] + '@parcel/watcher-wasm@2.6.0': resolution: {integrity: sha512-dtjbDxKSDPQ8AmA+pS4OFaHE1FKrjtGpLGBxw85uKFkRorjNbvDM/aFPgqosu40wprbp1xw2ZSxIKqghCUHe2w==} engines: {node: '>= 10.0.0'} @@ -2258,6 +2528,130 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@storybook/addon-mcp@0.7.0': + resolution: {integrity: sha512-f/IWGRMzWynBg5kDJ3DYvvnafuSX88kykGNFzzLOlkLVpfxEZoAmQF6AS47tw25GuH6EFIqSo6ZDBLHLVyZ/IQ==} + peerDependencies: + '@storybook/addon-vitest': ^0.0.0-0 || ^9.1.16 || ^10.0.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0 || ^10.5.0-0 + storybook: ^0.0.0-0 || ^9.1.16 || ^10.0.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0 || ^10.5.0-0 + peerDependenciesMeta: + '@storybook/addon-vitest': + optional: true + + '@storybook/addon-vitest@10.5.10': + resolution: {integrity: sha512-JNQ9DSkLfxC8qqytBCej91zBExIZ7z97B410U2zgfQPki4HkI9Ffz97a15f5yhVZ79ppIrZ/ssI+WcyWn0ykXQ==} + peerDependencies: + '@vitest/browser': ^3.0.0 || ^4.0.0 + '@vitest/browser-playwright': ^4.0.0 + '@vitest/runner': ^3.0.0 || ^4.0.0 + storybook: ^10.5.10 + vitest: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + '@vitest/browser': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/runner': + optional: true + vitest: + optional: true + + '@storybook/builder-vite@10.5.10': + resolution: {integrity: sha512-O4GgIP0tKLRueom3EmU3OaBUHKjNYj+jkOvmTIkn3PYTiWVkCuHqSKEs4ADvRyaQuLH+peHhFe4JtkNC9KbtrQ==} + peerDependencies: + storybook: ^10.5.10 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@storybook/csf-plugin@10.5.10': + resolution: {integrity: sha512-TaCLBrqVEr767+w58QDotDUiCTuE5cyRJuRcDlsKQyUIyGBv+lYD3lu8wBiVCYxgIjB/gu9HmqiC+0yx1rHzaw==} + peerDependencies: + esbuild: '*' + rollup: '*' + storybook: ^10.5.10 + 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==} + + '@storybook/icons@2.1.0': + resolution: {integrity: sha512-Fxh9vYpX9bQqFeHRiY8h2ApeRGDzRSMLwJwNZ/AIRqnyOKHxRKL+yFe+ctEkVJmuptRE9u1Hrn8ZZNHyfDKKNg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@storybook/mcp@0.8.0': + resolution: {integrity: sha512-G+XDgoWGrE98moXqKSee8fQyMQxoIWxRAbPG8r/HQ95pKodratevDqNyOgW+t6ZigM6AAVN1WHiFc5MI+hc8sg==} + + '@storybook/nextjs-vite@10.5.10': + resolution: {integrity: sha512-kDzhmW3pXWVI/trOcBu0XG7irieTBBdezL3gtmw6ZVyY7RJWWMlkzbVdXha76eWCM78kseLHafAxu8KgOsCI4w==} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@types/react-dom': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + next: ^14.1.0 || ^15.0.0 || ^16.0.0 + 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.10 + typescript: '*' + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + typescript: + optional: true + + '@storybook/react-dom-shim@10.5.10': + resolution: {integrity: sha512-rbu62ILo/VE3iXKmu+kWXFpD1H1Lwi0f19q/x7JnDsD2dxKS9w5znLEqPIq2qxpzi/wjjIb2iUP1cRG1d/9W5A==} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@types/react-dom': ^16.8.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.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.5.10 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@storybook/react-vite@10.5.10': + resolution: {integrity: sha512-xOztxefUnqKeuyvcnjspqmlDnER4cExL+liltrpdXLPJVqfFNr9lgM49FyEPajzsUVG9W/vHJWjbaQGGu1UsYQ==} + 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.10 + typescript: '>= 4.9.x' + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@storybook/react@10.5.10': + resolution: {integrity: sha512-4MBV5e1SXIMfPynLHzr+Mp0dwGv/FW1bklWAsS4ynBOAbC98W9p/I9vqBnUctsvE3BJkhzHQQyPwMHL5tTcHVA==} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@types/react-dom': ^16.8.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.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.5.10 + typescript: '>= 4.9.x' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + typescript: + optional: true + '@sveltejs/acorn-typescript@1.0.13': resolution: {integrity: sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==} peerDependencies: @@ -2273,6 +2667,10 @@ packages: resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@testing-library/jest-dom@7.0.1': resolution: {integrity: sha512-oMDTC3oA+6CXSO2JZnvOI7CA6oVub6kij5ggk9ohwye5slmkwxYDXcPOVxgMw/RQlticjtO0C1RZkR97HgrWMw==} engines: {node: '>=22', npm: '>=6', yarn: '>=1'} @@ -2298,6 +2696,32 @@ packages: '@types/react-dom': optional: true + '@testing-library/user-event@14.6.6': + resolution: {integrity: sha512-Jbs9FpkkIDw8FgSc6kOVsOv8JuuqGAL7J4X1oot77JxAoDlkNn2GRkd0aYRVuQ+pVQAiHWVkE4rX/dkF5fBiCw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@tmcp/adapter-valibot@0.1.6': + resolution: {integrity: sha512-drirZeNinhYLiRSMksN+m//u0ImFxtGRk1Vp425Xp/7CbBXFQdjAG+f7grssyHAukbVTGzmWsMMP6ejrGVErUA==} + peerDependencies: + tmcp: ^1.17.0 + valibot: ^1.1.0 + + '@tmcp/session-manager@0.2.2': + resolution: {integrity: sha512-UrCRpTsxh5XnMbplspvftEYboiZWgAiXqqAUbyFTHoHMJ0LoNDy8bQd0+7qtxtT4S5Qsnv650gvs/Nbec5NTCQ==} + peerDependencies: + tmcp: ^1.16.3 + + '@tmcp/transport-http@0.8.6': + resolution: {integrity: sha512-iLcxu+tEMbkVHbhFfyXQhxfPDDTfm+F0kEw8Xg/a1rm29s4cBg1vwcpbtk02XTxsdDh8RJ1AZkQwF9WDGeb/IA==} + peerDependencies: + '@tmcp/auth': ^0.3.3 || ^0.4.0 + tmcp: ^1.18.0 + peerDependenciesMeta: + '@tmcp/auth': + optional: true + '@tsconfig/node10@1.0.13': resolution: {integrity: sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==} @@ -2316,6 +2740,18 @@ packages: '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -2328,6 +2764,9 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/doctrine@0.0.9': + resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -2366,6 +2805,9 @@ packages: '@types/react@19.2.18': resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + '@types/resolve@1.20.6': + resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} + '@types/retry@0.12.2': resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} @@ -2569,6 +3011,11 @@ packages: cpu: [x64] os: [win32] + '@valibot/to-json-schema@1.7.1': + resolution: {integrity: sha512-3qkmU6KXWh8GIThEAW3kuRHPQBMjWkKy+Ppz3WkUucx53DTpOa6siMn4xDGSOhlVyMrDaJTCTMLYPZVAIk1P0A==} + peerDependencies: + valibot: ^1.4.0 + '@vercel/nft@0.29.4': resolution: {integrity: sha512-6lLqMNX3TuycBPABycx7A9F1bHQR7kiQln6abjFbPrf5C/05qHM9M5E4PeTE59c7z8g6vHnx1Ioihb2AQl7BTA==} engines: {node: '>=18'} @@ -2598,6 +3045,9 @@ packages: peerDependencies: vitest: 5.0.0-beta.7 + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/mocker@5.0.0-beta.7': resolution: {integrity: sha512-SczTGMNh6ksM5r8ogUwAf8zVbQPgbc6yvdZO9xBsTKT2rLUwpHn95Upod0s0Cpm3/bH898iqRBLXovRZSUbJBg==} peerDependencies: @@ -2609,9 +3059,15 @@ packages: vite: optional: true + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + '@vitest/pretty-format@5.0.0-beta.7': resolution: {integrity: sha512-mYUiYNj9BcF4BawuqncdUlR+zNNbWOWBNsp57jJon2Gq5R2fMqTYQJWHpfvkNqkAAgt/lC6bH5+JISjLIhHa4w==} + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/spy@5.0.0-beta.7': resolution: {integrity: sha512-NiE4frPDnDjFaMvaZaj4ASyxc/c2fvSQU8Fpo7D0YNj9AetE4hcA7ewRxhlrg6dHEOt1JhhCgy67e8SCXDh5VQ==} @@ -2620,6 +3076,9 @@ packages: peerDependencies: vitest: 5.0.0-beta.7 + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@5.0.0-beta.7': resolution: {integrity: sha512-DFjgVac0frIqGUZnQCd8lTGYfWnPJfzUNhOubZuvUMgzb+v0mdJjeMEcpmesujZIPasFOR+SzsQ4dS/BCEWGvg==} @@ -2638,6 +3097,9 @@ packages: '@vue/shared@3.5.41': resolution: {integrity: sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==} + '@webcontainer/env@1.1.1': + resolution: {integrity: sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng==} + '@whatwg-node/disposablestack@0.0.6': resolution: {integrity: sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==} engines: {node: '>=18.0.0'} @@ -2837,6 +3299,10 @@ packages: ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + ast-types@0.16.3: + resolution: {integrity: sha512-FvWoWYfSCM6kRxCSH+MGLHIKKGRL6A6AW7Zek2O32REPQRdg131428uRTKMBYAeRd3XXAaHDS60Wpri7CdKDrA==} + engines: {node: '>=4'} + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -3143,6 +3609,10 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -3170,6 +3640,10 @@ packages: chardet@2.2.0: resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + chevrotain@12.0.0: resolution: {integrity: sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==} engines: {node: '>=22.0.0'} @@ -3495,6 +3969,10 @@ packages: decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -3604,6 +4082,10 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -4035,6 +4517,11 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + eslint-plugin-storybook@10.5.10: + resolution: {integrity: sha512-NeOu3axmhNZfRuHRciFH0a/OVgvtAJLRHAwiJjcGkPAcElNmwEPp+pqoiwxytx7kHInz7mVVoBfPwVc8kPmOLw==} + peerDependencies: + eslint: '>=8' + eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4054,6 +4541,7 @@ packages: eslint@9.39.5: resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: jiti: '*' @@ -4061,6 +4549,9 @@ packages: jiti: optional: true + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + espree@10.4.0: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4458,6 +4949,9 @@ packages: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + gonzales-pe@4.3.0: resolution: {integrity: sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==} engines: {node: '>=0.6.0'} @@ -4606,6 +5100,11 @@ packages: image-meta@0.2.2: resolution: {integrity: sha512-3MOLanc3sb3LNGWQl1RlQlNWURE5g32aUphrDyFeCsxBTk08iE3VNe4CwsUZ0Qs1X+EfX0+r29Sxdpza4B+yRA==} + image-size@2.0.2: + resolution: {integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==} + engines: {node: '>=16.x'} + hasBin: true + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -4961,6 +5460,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-rpc-2.0@1.8.0: + resolution: {integrity: sha512-4nw+XlJbk5XokA7BtqHGu+0PEiUPfAkRzXXd1Cgjy1c3F2UI/3Gy7yr76wyJEv3X1F1SRGGF8xPAPPhelBiGmA==} + json-schema-ref-resolver@3.0.0: resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==} @@ -4988,6 +5490,9 @@ packages: engines: {node: '>=6'} hasBin: true + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + jsonpointer@5.0.1: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} @@ -5245,6 +5750,9 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -5525,6 +6033,9 @@ packages: resolution: {integrity: sha512-gN54ddmyzEg10orwZ2u4OOv+bjpMWdIl5jIkodK97bMq8QBSL5c0D7YX0lT1Ooz+99S7+PvFbnxzdjgHo1r41g==} engines: {node: '>=18.0.0'} + module-alias@2.3.4: + resolution: {integrity: sha512-bOclZt8hkpuGgSSoG07PKmvzTizROilUTvLNyrMqvlC9snhs7y7GzjNWAVbISIOlhCP1T14rH1PDAV9iNyBq/w==} + module-definition@6.0.2: resolution: {integrity: sha512-SvAU3lB0+Yjbq55yHY3wkRZBOh+fhU1SnIF3IFbTewv6mtAh7yUT8ACHAJ2mGIJ7tCes2QuCL/cl6m0JSZ/ArA==} engines: {node: '>=18'} @@ -5794,6 +6305,10 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + open@11.0.1: resolution: {integrity: sha512-NzwMUB6C1D0+Kd+9iMS/H4k+Ck3cTX6Ckyfr/gAGlmvSE1LUQZnEZvWBi4PYmMwH/S5SMeTXnE+9uAz8uF+pWw==} engines: {node: '>=20'} @@ -5814,6 +6329,13 @@ packages: resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} engines: {node: '>= 0.4'} + oxc-parser@0.127.0: + resolution: {integrity: sha512-bkgD4qHlN7WxLdX8bLXdaU54TtQtAIg/ZBAfm0aje/mo3MRDo3P0hZSgr4U7O3xfX+fQmR5AP04JS/TGcZLcFA==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-resolver@11.21.2: + resolution: {integrity: sha512-w5tLwYN3Zo24w5EeWJjJWZOwhYqTtC8PS2B1tIt7BZUuqTIcU07sQValbDw+rq7+AuAGzOHklgK+ifsy4lpXfw==} + p-event@6.0.1: resolution: {integrity: sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==} engines: {node: '>=16.17'} @@ -5954,6 +6476,10 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} @@ -6204,6 +6730,15 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-docgen-typescript@2.4.0: + resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==} + peerDependencies: + typescript: '>= 4.3.x' + + react-docgen@8.0.3: + resolution: {integrity: sha512-aEZ9qP+/M+58x2qgfSFEWH1BxLyHe5+qkLNJOZQb5iGS017jpbRnoKhNRrXPeA6RfBrZO5wZrT9DMC1UqE1f1w==} + engines: {node: ^20.9.0 || >=22} + react-dom@19.2.8: resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} peerDependencies: @@ -6279,6 +6814,10 @@ packages: real-require@1.0.0: resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} + recast@0.23.21: + resolution: {integrity: sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==} + engines: {node: '>= 4'} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -6352,6 +6891,11 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + resolve@2.0.0-next.7: resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} engines: {node: '>= 0.4'} @@ -6620,6 +7164,9 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + sqids@0.3.0: + resolution: {integrity: sha512-lOQK1ucVg+W6n3FhRwwSeUijxe93b51Bfz5PMRMihVf1iVkl82ePQG7V5vwrhzB11v0NtsR25PSZRGiSomJaJw==} + stable-hash@0.0.5: resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} @@ -6653,6 +7200,21 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + storybook@10.5.10: + resolution: {integrity: sha512-Rz8k9ejFHsi7lbtJTaxZlhCUz4GkbJIKEoKDjXeLfr/ZhXip73E6keKxW0KH8iGeKiCqHAbJCV4YIQrxTOLiig==} + hasBin: true + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + prettier: ^2 || ^3 + vite-plus: ^0.1.15 || ^0.2.0 + peerDependenciesMeta: + '@types/react': + optional: true + prettier: + optional: true + vite-plus: + optional: true + streamx@2.28.0: resolution: {integrity: sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==} @@ -6728,6 +7290,10 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} + engines: {node: '>=12'} + strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -6833,6 +7399,9 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@6.1.3: resolution: {integrity: sha512-8k25iNSZHnzkjp3nrpEmx6YkrTNs41PzOHYc28DR5Z2l/CId/Nzw+Ume/41jCeDDmCUMPuK5GkQ/VxJaJ2P6Qg==} engines: {node: '>=20.0.0'} @@ -6849,10 +7418,18 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + tinyrainbow@3.1.1: resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} engines: {node: '>=14.0.0'} + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + tldts-core@7.4.10: resolution: {integrity: sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==} @@ -6860,6 +7437,9 @@ packages: resolution: {integrity: sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==} hasBin: true + tmcp@1.20.0: + resolution: {integrity: sha512-dcDximKQBGqLP/aEAVA26HcWRHhKipstg1w0fbDDJ0HcFZ6lolLU1YYmStYEn/73f534i7WrDNcjRfRYdV9CoA==} + tmp-promise@3.0.3: resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} @@ -6916,6 +7496,10 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-dedent@2.3.0: + resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} + engines: {node: '>=6.10'} + ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true @@ -6930,6 +7514,17 @@ packages: '@swc/wasm': optional: true + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + deprecated: unmaintained + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -7070,6 +7665,10 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + unrs-resolver@1.12.2: resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} @@ -7159,18 +7758,34 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-template-matcher@1.1.2: + resolution: {integrity: sha512-uZc1h12jdO3m/R77SfTEOuo6VbMhgWznaawKpBjRGSJb7i91x5PgI37NQJtG+Cerxkk0yr1pylBY2qG1kQ+aEQ==} + urlpattern-polyfill@10.1.0: resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} urlpattern-polyfill@8.0.2: resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + valibot@1.2.0: + resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -7188,6 +7803,21 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite-plugin-storybook-nextjs@3.3.2: + resolution: {integrity: sha512-emY992wwyOfG5kDEmo7br/OWCz8YMOYhc/0YQo/vGdSqYQhTCRSvHHjjeysAr6co1F2cyoK50szd4nMitZ0k8w==} + peerDependencies: + next: ^14.1.0 || ^15.0.0 || ^16.0.0 + storybook: ^0.0.0-0 || ^9.0.0 || ^10.0.0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0 || ^10.5.0-0 || ^10.6.0-0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + vite@8.2.1: resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -7355,6 +7985,9 @@ packages: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + whatwg-mimetype@5.0.0: resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} engines: {node: '>=20'} @@ -7458,6 +8091,10 @@ packages: utf-8-validate: optional: true + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + wsl-utils@1.0.0: resolution: {integrity: sha512-Hl0ZOAs672vg+06kfujwRhoS6/jehvULrlFkuF2dRu6pHgA8U06h3xqNIqNNU1LTXPcedxByAR4GS6pwQK0mgA==} engines: {node: '>=20'} @@ -7582,16 +8219,16 @@ snapshots: '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.7': + '@babel/core@7.29.7(supports-color@10.2.2)': dependencies: '@babel/code-frame': 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/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helpers': 7.29.7 '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 @@ -7622,41 +8259,41 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/traverse': 7.29.8 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/traverse': 7.29.8(supports-color@10.2.2) semver: 6.3.1 transitivePeerDependencies: - supports-color '@babel/helper-globals@7.29.7': {} - '@babel/helper-member-expression-to-functions@7.29.7': + '@babel/helper-member-expression-to-functions@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.29.7': + '@babel/helper-module-imports@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -7666,18 +8303,18 @@ snapshots: '@babel/helper-plugin-utils@7.29.7': {} - '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + '@babel/helper-skip-transparent-expression-wrappers@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -7701,84 +8338,84 @@ snapshots: dependencies: '@babel/types': 7.29.8 - '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-display-name@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-display-name@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx-development@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx-development@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-pure-annotations@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) transitivePeerDependencies: - supports-color - '@babel/preset-react@7.29.7(@babel/core@7.29.7)': + '@babel/preset-react@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-validator-option': 7.29.7 - '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-pure-annotations': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-react-pure-annotations': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.29.7(@babel/core@7.29.7)': + '@babel/preset-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-validator-option': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -7790,7 +8427,7 @@ snapshots: '@babel/parser': 7.29.8 '@babel/types': 7.29.8 - '@babel/traverse@7.29.0': + '@babel/traverse@7.29.0(supports-color@10.2.2)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 @@ -7802,7 +8439,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/traverse@7.29.8': + '@babel/traverse@7.29.8(supports-color@10.2.2)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 @@ -7833,12 +8470,12 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 - '@better-auth/drizzle-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3))': + '@better-auth/drizzle-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))': dependencies: '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1) '@better-auth/utils': 0.4.2 optionalDependencies: - drizzle-orm: 1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3) + drizzle-orm: 1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3) '@better-auth/kysely-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2)(kysely@0.29.5)': dependencies: @@ -7999,21 +8636,48 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.0': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/core@1.9.2': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.0': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.11.3': dependencies: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.9.2': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + '@envelop/instrumentation@1.0.0': dependencies: '@whatwg-node/promise-helpers': 1.3.2 @@ -8175,19 +8839,19 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@2.7.0))': + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))': dependencies: - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5(jiti@2.7.0))': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))': dependencies: - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.2': + '@eslint/config-array@0.21.2(supports-color@10.2.2)': dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.3(supports-color@10.2.2) @@ -8203,7 +8867,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.6': + '@eslint/eslintrc@3.3.6(supports-color@10.2.2)': dependencies: ajv: 6.15.0 debug: 4.4.3(supports-color@10.2.2) @@ -8541,6 +9205,14 @@ snapshots: dependencies: minipass: 7.1.3 + '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': + dependencies: + glob: 13.0.6 + react-docgen-typescript: 2.4.0(typescript@6.0.3) + vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + optionalDependencies: + typescript: 6.0.3 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -8844,6 +9516,20 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0)': + dependencies: + '@emnapi/core': 1.11.0 + '@emnapi/runtime': 1.11.0 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@napi-rs/wasm-runtime@1.2.3(@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.3 + optional: true + '@neondatabase/serverless@1.1.0': {} '@netlify/ai@0.4.4': @@ -9038,7 +9724,7 @@ snapshots: parse-gitignore: 2.0.0 semver: 7.8.5 - '@netlify/dev@4.18.13': + '@netlify/dev@4.18.13(supports-color@10.2.2)': dependencies: '@netlify/ai': 0.4.4 '@netlify/blobs': 10.7.13(supports-color@10.2.2) @@ -9046,11 +9732,11 @@ snapshots: '@netlify/database-dev': 0.10.1 '@netlify/dev-utils': 5.0.0 '@netlify/edge-functions-dev': 1.0.24 - '@netlify/functions-dev': 1.3.5 + '@netlify/functions-dev': 1.3.5(supports-color@10.2.2) '@netlify/headers': 2.1.13 - '@netlify/images': 1.3.12(@netlify/blobs@10.7.13) + '@netlify/images': 1.3.12(@netlify/blobs@10.7.13(supports-color@10.2.2)) '@netlify/redirects': 3.1.15 - '@netlify/runtime': 4.1.29 + '@netlify/runtime': 4.1.29(supports-color@10.2.2) '@netlify/static': 3.1.12 ulid: 3.0.2 transitivePeerDependencies: @@ -9142,7 +9828,7 @@ snapshots: dependencies: '@netlify/types': 2.8.0 - '@netlify/functions-dev@1.3.5': + '@netlify/functions-dev@1.3.5(supports-color@10.2.2)': dependencies: '@netlify/blobs': 10.7.13(supports-color@10.2.2) '@netlify/dev-utils': 5.0.0 @@ -9150,7 +9836,7 @@ snapshots: '@netlify/zip-it-and-ship-it': 15.3.4(supports-color@10.2.2) cron-parser: 4.9.0 decache: 4.6.2 - extract-zip: 2.0.1 + extract-zip: 2.0.1(supports-color@10.2.2) is-stream: 4.0.1 jwt-decode: 4.0.0 lambda-local: 2.2.0 @@ -9198,9 +9884,9 @@ snapshots: dependencies: '@netlify/headers-parser': 10.1.1 - '@netlify/images@1.3.12(@netlify/blobs@10.7.13)': + '@netlify/images@1.3.12(@netlify/blobs@10.7.13(supports-color@10.2.2))': dependencies: - ipx: 3.1.1(@netlify/blobs@10.7.13) + ipx: 3.1.1(@netlify/blobs@10.7.13(supports-color@10.2.2)) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -9316,7 +10002,7 @@ snapshots: '@netlify/runtime-utils@3.0.0': {} - '@netlify/runtime@4.1.29': + '@netlify/runtime@4.1.29(supports-color@10.2.2)': dependencies: '@netlify/blobs': 10.7.13(supports-color@10.2.2) '@netlify/cache': 3.4.10 @@ -9380,11 +10066,13 @@ snapshots: '@next/env@16.3.0': {} + '@next/env@16.3.0-preview.5': {} + '@next/env@16.3.1': {} - '@next/eslint-plugin-next@16.3.1(eslint@9.39.5(jiti@2.7.0))': + '@next/eslint-plugin-next@16.3.1(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2)) fast-glob: 3.3.1 transitivePeerDependencies: - eslint @@ -9585,8 +10273,135 @@ snapshots: '@opentelemetry/semantic-conventions@1.43.0': {} + '@oxc-parser/binding-android-arm-eabi@0.127.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.127.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.127.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.127.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.127.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.127.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.127.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.127.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.127.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.127.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.127.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.127.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.127.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.127.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.127.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.127.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.127.0': + dependencies: + '@emnapi/core': 1.9.2 + '@emnapi/runtime': 1.9.2 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.127.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.127.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.127.0': + optional: true + + '@oxc-project/types@0.127.0': {} + '@oxc-project/types@0.144.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.21.2': + optional: true + + '@oxc-resolver/binding-android-arm64@11.21.2': + optional: true + + '@oxc-resolver/binding-darwin-arm64@11.21.2': + optional: true + + '@oxc-resolver/binding-darwin-x64@11.21.2': + optional: true + + '@oxc-resolver/binding-freebsd-x64@11.21.2': + optional: true + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.21.2': + optional: true + + '@oxc-resolver/binding-linux-arm-musleabihf@11.21.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@11.21.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@11.21.2': + optional: true + + '@oxc-resolver/binding-linux-ppc64-gnu@11.21.2': + optional: true + + '@oxc-resolver/binding-linux-riscv64-gnu@11.21.2': + optional: true + + '@oxc-resolver/binding-linux-riscv64-musl@11.21.2': + optional: true + + '@oxc-resolver/binding-linux-s390x-gnu@11.21.2': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@11.21.2': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@11.21.2': + optional: true + + '@oxc-resolver/binding-openharmony-arm64@11.21.2': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@11.21.2': + dependencies: + '@emnapi/core': 1.11.0 + '@emnapi/runtime': 1.11.0 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0) + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@11.21.2': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@11.21.2': + optional: true + '@parcel/watcher-wasm@2.6.0': dependencies: is-glob: 4.0.3 @@ -9609,7 +10424,7 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@pnpm/tabtab@0.5.4': + '@pnpm/tabtab@0.5.4(supports-color@10.2.2)': dependencies: debug: 4.4.3(supports-color@10.2.2) enquirer: 2.4.1 @@ -9631,10 +10446,10 @@ snapshots: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@react-email/ui@6.9.2(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@react-email/ui@6.9.2(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: esbuild: 0.28.1 - next: 16.3.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + next: 16.3.0(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) transitivePeerDependencies: - '@babel/core' - '@opentelemetry/api' @@ -9688,44 +10503,181 @@ snapshots: '@rolldown/binding-win32-x64-msvc@1.2.4': optional: true - '@rolldown/pluginutils@1.0.1': {} - - '@rollup/pluginutils@5.4.0': + '@rolldown/pluginutils@1.0.1': {} + + '@rollup/pluginutils@5.4.0': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.5 + + '@rtsao/scc@1.1.0': {} + + '@sec-ant/readable-stream@0.4.1': {} + + '@selderee/plugin-htmlparser2@0.11.0': + dependencies: + domhandler: 5.0.3 + selderee: 0.11.0 + + '@sindresorhus/merge-streams@2.3.0': {} + + '@sindresorhus/slugify@2.2.1': + dependencies: + '@sindresorhus/transliterate': 1.6.0 + escape-string-regexp: 5.0.0 + + '@sindresorhus/transliterate@1.6.0': + dependencies: + escape-string-regexp: 5.0.0 + + '@so-ric/colorspace@1.1.6': + dependencies: + color: 5.0.3 + text-hex: 1.0.0 + + '@socket.io/component-emitter@3.1.2': {} + + '@stablelib/base64@1.0.1': {} + + '@standard-schema/spec@1.1.0': {} + + '@storybook/addon-mcp@0.7.0(@storybook/addon-vitest@10.5.10(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/browser@5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-beta.7))(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(typescript@6.0.3)': + dependencies: + '@storybook/mcp': 0.8.0(typescript@6.0.3) + '@tmcp/adapter-valibot': 0.1.6(tmcp@1.20.0(typescript@6.0.3))(valibot@1.2.0(typescript@6.0.3)) + '@tmcp/transport-http': 0.8.6(tmcp@1.20.0(typescript@6.0.3)) + picoquery: 2.5.0 + storybook: 10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8) + tmcp: 1.20.0(typescript@6.0.3) + valibot: 1.2.0(typescript@6.0.3) + optionalDependencies: + '@storybook/addon-vitest': 10.5.10(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/browser@5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-beta.7) + transitivePeerDependencies: + - '@tmcp/auth' + - typescript + + '@storybook/addon-vitest@10.5.10(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/browser@5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-beta.7)': + dependencies: + '@storybook/global': 5.0.0 + '@storybook/icons': 2.1.0(react@19.2.8) + storybook: 10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8) + optionalDependencies: + '@vitest/browser': 5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7) + '@vitest/browser-playwright': 5.0.0-beta.7(playwright@1.62.1)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7) + vitest: 5.0.0-beta.7(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/ui@5.0.0-beta.7)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + transitivePeerDependencies: + - react + optional: true + + '@storybook/builder-vite@10.5.10(esbuild@0.28.1)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: - '@types/estree': 1.0.9 - estree-walker: 2.0.2 - picomatch: 4.0.5 - - '@rtsao/scc@1.1.0': {} - - '@sec-ant/readable-stream@0.4.1': {} + '@storybook/csf-plugin': 10.5.10(esbuild@0.28.1)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8) + ts-dedent: 2.3.0 + vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + transitivePeerDependencies: + - esbuild + - rollup + - webpack - '@selderee/plugin-htmlparser2@0.11.0': + '@storybook/csf-plugin@10.5.10(esbuild@0.28.1)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: - domhandler: 5.0.3 - selderee: 0.11.0 + storybook: 10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8) + unplugin: 2.3.11 + optionalDependencies: + esbuild: 0.28.1 + vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) - '@sindresorhus/merge-streams@2.3.0': {} + '@storybook/global@5.0.0': {} - '@sindresorhus/slugify@2.2.1': + '@storybook/icons@2.1.0(react@19.2.8)': dependencies: - '@sindresorhus/transliterate': 1.6.0 - escape-string-regexp: 5.0.0 + react: 19.2.8 - '@sindresorhus/transliterate@1.6.0': + '@storybook/mcp@0.8.0(typescript@6.0.3)': dependencies: - escape-string-regexp: 5.0.0 + '@tmcp/adapter-valibot': 0.1.6(tmcp@1.20.0(typescript@6.0.3))(valibot@1.2.0(typescript@6.0.3)) + '@tmcp/transport-http': 0.8.6(tmcp@1.20.0(typescript@6.0.3)) + tmcp: 1.20.0(typescript@6.0.3) + valibot: 1.2.0(typescript@6.0.3) + transitivePeerDependencies: + - '@tmcp/auth' + - typescript - '@so-ric/colorspace@1.1.6': + '@storybook/nextjs-vite@10.5.10(@babel/core@7.29.7(supports-color@10.2.2))(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.1)(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(supports-color@10.2.2)(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: - color: 5.0.3 - text-hex: 1.0.0 + '@storybook/builder-vite': 10.5.10(esbuild@0.28.1)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + '@storybook/react': 10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(supports-color@10.2.2)(typescript@6.0.3) + '@storybook/react-vite': 10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(supports-color@10.2.2)(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + next: 16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + storybook: 10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8) + styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.8) + vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + vite-plugin-storybook-nextjs: 3.3.2(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(supports-color@10.2.2)(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + optionalDependencies: + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) + typescript: 6.0.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + - esbuild + - rollup + - supports-color + - webpack - '@socket.io/component-emitter@3.1.2': {} + '@storybook/react-dom-shim@10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))': + dependencies: + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + storybook: 10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@stablelib/base64@1.0.1': {} + '@storybook/react-vite@10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(supports-color@10.2.2)(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': + dependencies: + '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + '@rollup/pluginutils': 5.4.0 + '@storybook/builder-vite': 10.5.10(esbuild@0.28.1)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + '@storybook/react': 10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(supports-color@10.2.2)(typescript@6.0.3) + empathic: 2.0.1 + magic-string: 0.30.21 + react: 19.2.8 + react-docgen: 8.0.3(supports-color@10.2.2) + react-dom: 19.2.8(react@19.2.8) + resolve: 1.22.12 + storybook: 10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8) + tsconfig-paths: 4.2.0 + vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + - esbuild + - rollup + - supports-color + - webpack - '@standard-schema/spec@1.1.0': {} + '@storybook/react@10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(supports-color@10.2.2)(typescript@6.0.3)': + dependencies: + '@storybook/global': 5.0.0 + '@storybook/react-dom-shim': 10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8)) + react: 19.2.8 + react-docgen: 8.0.3(supports-color@10.2.2) + react-docgen-typescript: 2.4.0(typescript@6.0.3) + react-dom: 19.2.8(react@19.2.8) + storybook: 10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color '@sveltejs/acorn-typescript@1.0.13(acorn@8.18.0)': dependencies: @@ -9750,6 +10702,15 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.5.0 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + '@testing-library/jest-dom@7.0.1(@testing-library/dom@10.4.1)(vitest@5.0.0-beta.7)': dependencies: '@adobe/css-tools': 4.5.0 @@ -9772,6 +10733,27 @@ snapshots: '@types/react': 19.2.18 '@types/react-dom': 19.2.4(@types/react@19.2.18) + '@testing-library/user-event@14.6.6(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + + '@tmcp/adapter-valibot@0.1.6(tmcp@1.20.0(typescript@6.0.3))(valibot@1.2.0(typescript@6.0.3))': + dependencies: + '@standard-schema/spec': 1.1.0 + '@valibot/to-json-schema': 1.7.1(valibot@1.2.0(typescript@6.0.3)) + tmcp: 1.20.0(typescript@6.0.3) + valibot: 1.2.0(typescript@6.0.3) + + '@tmcp/session-manager@0.2.2(tmcp@1.20.0(typescript@6.0.3))': + dependencies: + tmcp: 1.20.0(typescript@6.0.3) + + '@tmcp/transport-http@0.8.6(tmcp@1.20.0(typescript@6.0.3))': + dependencies: + '@tmcp/session-manager': 0.2.2(tmcp@1.20.0(typescript@6.0.3)) + esm-env: 1.2.2 + tmcp: 1.20.0(typescript@6.0.3) + '@tsconfig/node10@1.0.13': {} '@tsconfig/node12@1.0.11': {} @@ -9787,6 +10769,27 @@ snapshots: '@types/aria-query@5.0.4': {} + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.8 + '@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.8 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.8 + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -9802,6 +10805,8 @@ snapshots: '@types/deep-eql@4.0.2': {} + '@types/doctrine@0.0.9': {} + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.9 @@ -9840,6 +10845,8 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/resolve@1.20.6': {} + '@types/retry@0.12.2': {} '@types/triple-beam@1.3.5': {} @@ -9859,15 +10866,15 @@ snapshots: '@types/node': 24.13.3 optional: true - '@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.67.0 - '@typescript-eslint/type-utils': 8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.67.0 - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) ignore: 7.0.6 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -9875,14 +10882,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.67.0 '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/typescript-estree': 8.67.0(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.67.0 debug: 4.4.3(supports-color@10.2.2) - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -9896,7 +10903,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.67.0(typescript@6.0.3)': + '@typescript-eslint/project-service@8.67.0(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@6.0.3) '@typescript-eslint/types': 8.67.0 @@ -9918,13 +10925,13 @@ snapshots: dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/typescript-estree': 8.67.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) debug: 4.4.3(supports-color@10.2.2) - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -9947,9 +10954,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.67.0(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.67.0(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.67.0(typescript@6.0.3) + '@typescript-eslint/project-service': 8.67.0(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@6.0.3) '@typescript-eslint/types': 8.67.0 '@typescript-eslint/visitor-keys': 8.67.0 @@ -9962,13 +10969,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/utils@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2)) '@typescript-eslint/scope-manager': 8.67.0 '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/typescript-estree': 8.67.0(typescript@6.0.3) - eslint: 9.39.5(jiti@2.7.0) + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@10.2.2)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -10050,6 +11057,10 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.12.2': optional: true + '@valibot/to-json-schema@1.7.1(valibot@1.2.0(typescript@6.0.3))': + dependencies: + valibot: 1.2.0(typescript@6.0.3) + '@vercel/nft@0.29.4(supports-color@10.2.2)': dependencies: '@mapbox/node-pre-gyp': 2.0.3(supports-color@10.2.2) @@ -10105,6 +11116,14 @@ snapshots: - utf-8-validate - vite + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + '@vitest/mocker@5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -10114,10 +11133,18 @@ snapshots: optionalDependencies: vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + '@vitest/pretty-format@5.0.0-beta.7': dependencies: tinyrainbow: 3.1.1 + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + '@vitest/spy@5.0.0-beta.7': {} '@vitest/ui@5.0.0-beta.7(vitest@5.0.0-beta.7)': @@ -10131,6 +11158,12 @@ snapshots: tinyrainbow: 3.1.1 vitest: 5.0.0-beta.7(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/ui@5.0.0-beta.7)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + '@vitest/utils@5.0.0-beta.7': dependencies: '@vitest/pretty-format': 5.0.0-beta.7 @@ -10169,6 +11202,8 @@ snapshots: '@vue/shared@3.5.41': {} + '@webcontainer/env@1.1.1': {} + '@whatwg-node/disposablestack@0.0.6': dependencies: '@whatwg-node/promise-helpers': 1.3.2 @@ -10396,6 +11431,10 @@ snapshots: ast-types-flow@0.0.8: {} + ast-types@0.16.3: + dependencies: + tslib: 2.8.1 + async-function@1.0.0: {} async-sema@3.1.1: {} @@ -10409,17 +11448,17 @@ snapshots: stubborn-fs: 2.0.0 when-exit: 2.1.5 - auth@1.7.1(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(chokidar@5.0.0)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1)(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-beta.7): + auth@1.7.1(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(chokidar@5.0.0)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1)(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2)(vitest@5.0.0-beta.7): dependencies: - '@babel/core': 7.29.7 - '@babel/preset-react': 7.29.7(@babel/core@7.29.7) - '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/preset-react': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1) '@better-auth/telemetry': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) '@better-auth/utils': 0.4.2 '@clack/prompts': 1.7.0 '@mrleebo/prisma-ast': 0.16.0 - better-auth: 1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-beta.7) + better-auth: 1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-beta.7) c12: 4.0.0-beta.5(chokidar@5.0.0)(dotenv@17.4.2)(jiti@2.7.0) chalk: 5.6.2 commander: 15.0.0 @@ -10535,10 +11574,10 @@ snapshots: jsonpointer: 5.0.1 leven: 3.1.0 - better-auth@1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-beta.7): + better-auth@1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-beta.7): dependencies: '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1) - '@better-auth/drizzle-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3)) + '@better-auth/drizzle-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3)) '@better-auth/kysely-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2)(kysely@0.29.5) '@better-auth/memory-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2) '@better-auth/mongo-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2) @@ -10556,8 +11595,8 @@ snapshots: zod: 4.4.3 optionalDependencies: drizzle-kit: 1.0.0-rc.4 - drizzle-orm: 1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3) - next: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + drizzle-orm: 1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3) + next: 16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) pg: 8.23.0 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) @@ -10590,7 +11629,7 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@2.3.0: + body-parser@2.3.0(supports-color@10.2.2): dependencies: bytes: 3.1.2 content-type: 2.1.0 @@ -10708,6 +11747,14 @@ snapshots: ccount@2.0.1: {} + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + chai@6.2.2: {} chalk@4.1.2: @@ -10727,6 +11774,8 @@ snapshots: chardet@2.2.0: {} + check-error@2.1.3: {} + chevrotain@12.0.0: dependencies: '@chevrotain/cst-dts-gen': 12.0.0 @@ -11005,9 +12054,11 @@ snapshots: debounce@2.2.0: {} - debug@3.2.7: + debug@3.2.7(supports-color@10.2.2): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 debug@4.4.3(supports-color@10.2.2): dependencies: @@ -11026,6 +12077,8 @@ snapshots: dependencies: character-entities: 2.0.2 + deep-eql@5.0.2: {} + deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -11135,6 +12188,10 @@ snapshots: dependencies: esutils: 2.0.3 + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + dom-accessibility-api@0.5.16: {} dom-accessibility-api@0.6.3: {} @@ -11177,11 +12234,12 @@ snapshots: get-tsconfig: 4.14.3 jiti: 2.7.0 - drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(zod@4.4.3): + drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3): optionalDependencies: '@neondatabase/serverless': 1.1.0 '@opentelemetry/api': 1.9.1 pg: 8.23.0 + valibot: 1.2.0(typescript@6.0.3) zod: 4.4.3 dunder-proto@1.0.1: @@ -11218,7 +12276,7 @@ snapshots: engine.io-parser@5.2.3: {} - engine.io@6.6.9: + engine.io@6.6.9(supports-color@10.2.2): dependencies: '@types/cors': 2.8.19 '@types/node': 24.13.3 @@ -11456,18 +12514,18 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-next@16.3.1(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3): + eslint-config-next@16.3.1(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@next/eslint-plugin-next': 16.3.1(eslint@9.39.5(jiti@2.7.0)) - eslint: 9.39.5(jiti@2.7.0) - eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5(jiti@2.7.0)) - eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@2.7.0)) - eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5(jiti@2.7.0)) + '@next/eslint-plugin-next': 16.3.1(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2)) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) + eslint-import-resolver-node: 0.3.10(supports-color@10.2.2) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) globals: 16.4.0 - typescript-eslint: 8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + typescript-eslint: 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) optionalDependencies: typescript: 6.0.3 transitivePeerDependencies: @@ -11476,56 +12534,56 @@ snapshots: - eslint-plugin-import-x - supports-color - eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)): + eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) - eslint-import-resolver-node@0.3.10: + eslint-import-resolver-node@0.3.10(supports-color@10.2.2): dependencies: - debug: 3.2.7 + debug: 3.2.7(supports-color@10.2.2) is-core-module: 2.16.2 resolve: 2.0.0-next.7 transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3(supports-color@10.2.2) - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) get-tsconfig: 4.14.3 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.17 unrs-resolver: 1.12.2 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - eslint-module-utils@2.14.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)): + eslint-module-utils@2.14.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - debug: 3.2.7 + debug: 3.2.7(supports-color@10.2.2) optionalDependencies: - '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) - eslint: 9.39.5(jiti@2.7.0) - eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) + eslint-import-resolver-node: 0.3.10(supports-color@10.2.2) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 array.prototype.findlastindex: 1.2.6 array.prototype.flat: 1.3.3 array.prototype.flatmap: 1.3.3 - debug: 3.2.7 + debug: 3.2.7(supports-color@10.2.2) doctrine: 2.1.0 - eslint: 9.39.5(jiti@2.7.0) - eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) + eslint-import-resolver-node: 0.3.10(supports-color@10.2.2) + eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) hasown: 2.0.4 is-core-module: 2.16.2 is-glob: 4.0.3 @@ -11537,13 +12595,13 @@ snapshots: string.prototype.trimend: 1.0.10 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.5(jiti@2.7.0)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2)): dependencies: aria-query: 5.3.2 array-includes: 3.1.9 @@ -11553,7 +12611,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) hasown: 2.0.4 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -11562,18 +12620,18 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@7.1.1(eslint@9.39.5(jiti@2.7.0)): + eslint-plugin-react-hooks@7.1.1(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/parser': 7.29.8 - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) hermes-parser: 0.25.1 zod: 4.4.3 zod-validation-error: 4.0.2(zod@4.4.3) transitivePeerDependencies: - supports-color - eslint-plugin-react@7.37.5(eslint@9.39.5(jiti@2.7.0)): + eslint-plugin-react@7.37.5(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 @@ -11581,7 +12639,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.4.0 - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) estraverse: 5.3.0 hasown: 2.0.4 jsx-ast-utils: 3.3.5 @@ -11595,6 +12653,15 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 + eslint-plugin-storybook@10.5.10(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): + dependencies: + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + - typescript + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -11606,14 +12673,14 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@9.39.5(jiti@2.7.0): + eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.2 + '@eslint/config-array': 0.21.2(supports-color@10.2.2) '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.6 + '@eslint/eslintrc': 3.3.6(supports-color@10.2.2) '@eslint/js': 9.39.5 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.8 @@ -11647,6 +12714,8 @@ snapshots: transitivePeerDependencies: - supports-color + esm-env@1.2.2: {} + espree@10.4.0: dependencies: acorn: 8.18.0 @@ -11719,10 +12788,10 @@ snapshots: dependencies: on-headers: 1.1.0 - express@5.2.1: + express@5.2.1(supports-color@10.2.2): dependencies: accepts: 2.0.0 - body-parser: 2.3.0 + body-parser: 2.3.0(supports-color@10.2.2) content-disposition: 1.1.0 content-type: 1.0.5 cookie: 0.7.2 @@ -11732,7 +12801,7 @@ snapshots: encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 2.1.1 + finalhandler: 2.1.1(supports-color@10.2.2) fresh: 2.0.0 http-errors: 2.0.1 merge-descriptors: 2.0.0 @@ -11743,9 +12812,9 @@ snapshots: proxy-addr: 2.0.7 qs: 6.15.3 range-parser: 1.3.0 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 + router: 2.2.0(supports-color@10.2.2) + send: 1.2.1(supports-color@10.2.2) + serve-static: 2.2.1(supports-color@10.2.2) statuses: 2.0.2 type-is: 2.1.0 vary: 1.1.2 @@ -11756,7 +12825,7 @@ snapshots: extend@3.0.2: {} - extract-zip@2.0.1: + extract-zip@2.0.1(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) get-stream: 5.2.0 @@ -11890,7 +12959,7 @@ snapshots: filter-obj@6.1.0: {} - finalhandler@2.1.1: + finalhandler@2.1.1(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) encodeurl: 2.0.0 @@ -11938,7 +13007,7 @@ snapshots: dependencies: from2: 2.3.0 - follow-redirects@1.16.0(debug@4.4.3): + follow-redirects@1.16.0(debug@4.4.3(supports-color@10.2.2)): optionalDependencies: debug: 4.4.3(supports-color@10.2.2) @@ -12099,6 +13168,8 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.3.0 + globrex@0.1.2: {} + gonzales-pe@4.3.0: dependencies: minimist: 1.2.8 @@ -12145,7 +13216,7 @@ snapshots: dependencies: function-bind: 1.1.2 - hast-util-to-jsx-runtime@2.3.6: + hast-util-to-jsx-runtime@2.3.6(supports-color@10.2.2): dependencies: '@types/estree': 1.0.9 '@types/hast': 3.0.5 @@ -12154,9 +13225,9 @@ snapshots: devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-mdxjs-esm: 2.0.1 + mdast-util-mdx-expression: 2.0.1(supports-color@10.2.2) + mdast-util-mdx-jsx: 3.2.0(supports-color@10.2.2) + mdast-util-mdxjs-esm: 2.0.1(supports-color@10.2.2) property-information: 7.2.0 space-separated-tokens: 2.0.2 style-to-js: 1.1.21 @@ -12233,21 +13304,21 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 - http-proxy-middleware@3.0.7: + http-proxy-middleware@3.0.7(supports-color@10.2.2): dependencies: '@types/http-proxy': 1.17.17 debug: 4.4.3(supports-color@10.2.2) - http-proxy: 1.18.1(debug@4.4.3) + http-proxy: 1.18.1(debug@4.4.3(supports-color@10.2.2)) is-glob: 4.0.3 is-plain-object: 5.0.0 micromatch: 4.0.8 transitivePeerDependencies: - supports-color - http-proxy@1.18.1(debug@4.4.3): + http-proxy@1.18.1(debug@4.4.3(supports-color@10.2.2)): dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.16.0(debug@4.4.3) + follow-redirects: 1.16.0(debug@4.4.3(supports-color@10.2.2)) requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -12261,7 +13332,7 @@ snapshots: transitivePeerDependencies: - supports-color - https-proxy-agent@8.0.0: + https-proxy-agent@8.0.0(supports-color@10.2.2): dependencies: agent-base: 8.0.0 debug: 4.4.3(supports-color@10.2.2) @@ -12284,6 +13355,8 @@ snapshots: image-meta@0.2.2: {} + image-size@2.0.2: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -12350,7 +13423,7 @@ snapshots: ipaddr.js@2.5.0: {} - ipx@3.1.1(@netlify/blobs@10.7.13): + ipx@3.1.1(@netlify/blobs@10.7.13(supports-color@10.2.2)): dependencies: '@fastify/accept-negotiator': 2.1.0 citty: 0.1.6 @@ -12366,7 +13439,7 @@ snapshots: sharp: 0.34.5 svgo: 4.0.2 ufo: 1.6.4 - unstorage: 1.17.5(@netlify/blobs@10.7.13) + unstorage: 1.17.5(@netlify/blobs@10.7.13(supports-color@10.2.2)) xss: 1.0.15 transitivePeerDependencies: - '@azure/app-configuration' @@ -12654,6 +13727,8 @@ snapshots: json-buffer@3.0.1: {} + json-rpc-2.0@1.8.0: {} + json-schema-ref-resolver@3.0.0: dependencies: dequal: 2.0.3 @@ -12674,6 +13749,8 @@ snapshots: json5@2.2.3: {} + jsonc-parser@3.3.1: {} + jsonpointer@5.0.1: {} jsonwebtoken@9.0.3: @@ -12921,6 +13998,8 @@ snapshots: dependencies: js-tokens: 4.0.0 + loupe@3.2.1: {} + lru-cache@10.4.3: {} lru-cache@11.5.2: {} @@ -12969,14 +14048,14 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - mdast-util-from-markdown@2.0.3: + mdast-util-from-markdown@2.0.3(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 - micromark: 4.0.2 + micromark: 4.0.2(supports-color@10.2.2) micromark-util-decode-numeric-character-reference: 2.0.2 micromark-util-decode-string: 2.0.1 micromark-util-normalize-identifier: 2.0.1 @@ -12994,67 +14073,67 @@ snapshots: mdast-util-find-and-replace: 3.0.2 micromark-util-character: 2.1.1 - mdast-util-gfm-footnote@2.1.0: + mdast-util-gfm-footnote@2.1.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 micromark-util-normalize-identifier: 2.0.1 transitivePeerDependencies: - supports-color - mdast-util-gfm-strikethrough@2.0.0: + mdast-util-gfm-strikethrough@2.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm-table@2.0.0: + mdast-util-gfm-table@2.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 markdown-table: 3.0.4 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm-task-list-item@2.0.0: + mdast-util-gfm-task-list-item@2.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm@3.1.0: + mdast-util-gfm@3.1.0(supports-color@10.2.2): dependencies: - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-gfm-autolink-literal: 2.0.1 - mdast-util-gfm-footnote: 2.1.0 - mdast-util-gfm-strikethrough: 2.0.0 - mdast-util-gfm-table: 2.0.0 - mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-gfm-footnote: 2.1.0(supports-color@10.2.2) + mdast-util-gfm-strikethrough: 2.0.0(supports-color@10.2.2) + mdast-util-gfm-table: 2.0.0(supports-color@10.2.2) + mdast-util-gfm-task-list-item: 2.0.0(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-mdx-expression@2.0.1: + mdast-util-mdx-expression@2.0.1(supports-color@10.2.2): dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-mdx-jsx@3.2.0: + mdast-util-mdx-jsx@3.2.0(supports-color@10.2.2): dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.5 @@ -13062,7 +14141,7 @@ snapshots: '@types/unist': 3.0.3 ccount: 2.0.1 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 parse-entities: 4.0.2 stringify-entities: 4.0.4 @@ -13071,13 +14150,13 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-mdxjs-esm@2.0.1: + mdast-util-mdxjs-esm@2.0.1(supports-color@10.2.2): dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -13307,7 +14386,7 @@ snapshots: micromark-util-types@2.0.2: {} - micromark@4.0.2: + micromark@4.0.2(supports-color@10.2.2): dependencies: '@types/debug': 4.1.13 debug: 4.4.3(supports-color@10.2.2) @@ -13378,6 +14457,8 @@ snapshots: modern-tar@0.8.4: {} + module-alias@2.3.4: {} + module-definition@6.0.2: dependencies: ast-module-types: 6.0.2 @@ -13420,7 +14501,7 @@ snapshots: negotiator@1.0.0: {} - netlify-cli@27.1.2(@types/node@24.13.3)(picomatch@4.0.5): + netlify-cli@27.1.2(@types/node@24.13.3)(picomatch@4.0.5)(supports-color@10.2.2): dependencies: '@fastify/static': 10.1.3 '@netlify/ai': 0.4.4 @@ -13429,19 +14510,19 @@ snapshots: '@netlify/build': 36.3.5(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(picomatch@4.0.5) '@netlify/build-info': 11.3.0 '@netlify/config': 25.2.2 - '@netlify/dev': 4.18.13 + '@netlify/dev': 4.18.13(supports-color@10.2.2) '@netlify/dev-utils': 5.0.0 '@netlify/edge-bundler': 16.0.3 '@netlify/edge-functions': 3.0.8 '@netlify/edge-functions-bootstrap': 3.2.0 '@netlify/headers-parser': 10.1.1 - '@netlify/images': 1.3.12(@netlify/blobs@10.7.13) + '@netlify/images': 1.3.12(@netlify/blobs@10.7.13(supports-color@10.2.2)) '@netlify/local-functions-proxy': 2.0.3 '@netlify/redirect-parser': 16.1.0 '@netlify/zip-it-and-ship-it': 15.3.4(supports-color@10.2.2) '@octokit/rest': 22.0.1 '@opentelemetry/api': 1.9.1 - '@pnpm/tabtab': 0.5.4 + '@pnpm/tabtab': 0.5.4(supports-color@10.2.2) ansi-escapes: 7.3.0 ansi-to-html: 0.7.2 ascii-table: 0.0.9 @@ -13464,7 +14545,7 @@ snapshots: envinfo: 7.21.0 etag: 1.8.1 execa: 5.1.1 - express: 5.2.1 + express: 5.2.1(supports-color@10.2.2) express-logging: 1.1.1 fastest-levenshtein: 1.0.16 fastify: 5.12.1 @@ -13474,9 +14555,9 @@ snapshots: get-port: 5.1.1 git-repo-info: 2.1.1 gitconfiglocal: 2.1.0 - http-proxy: 1.18.1(debug@4.4.3) - http-proxy-middleware: 3.0.7 - https-proxy-agent: 8.0.0 + http-proxy: 1.18.1(debug@4.4.3(supports-color@10.2.2)) + http-proxy-middleware: 3.0.7(supports-color@10.2.2) + https-proxy-agent: 8.0.0(supports-color@10.2.2) inquirer: 8.2.7(@types/node@24.13.3) inquirer-autocomplete-prompt: 1.4.0(inquirer@8.2.7(@types/node@24.13.3)) is-docker: 4.0.0 @@ -13556,7 +14637,7 @@ snapshots: netlify-redirector@0.5.0: {} - next@16.3.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + next@16.3.0(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: '@next/env': 16.3.0 '@swc/helpers': 0.5.15 @@ -13565,7 +14646,7 @@ snapshots: postcss: 8.5.23 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.8) + styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.8) optionalDependencies: '@next/swc-darwin-arm64': 16.3.0 '@next/swc-darwin-x64': 16.3.0 @@ -13582,7 +14663,7 @@ snapshots: - '@types/node' - babel-plugin-macros - next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: '@next/env': 16.3.1 '@swc/helpers': 0.5.23 @@ -13591,7 +14672,7 @@ snapshots: postcss: 8.5.23 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.8) + styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.8) optionalDependencies: '@next/swc-darwin-arm64': 16.3.1 '@next/swc-darwin-x64': 16.3.1 @@ -13772,6 +14853,13 @@ snapshots: dependencies: mimic-function: 5.0.1 + open@10.2.0: + dependencies: + default-browser: 5.5.1 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + open@11.0.1: dependencies: default-browser: 5.5.1 @@ -13814,6 +14902,53 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + oxc-parser@0.127.0: + dependencies: + '@oxc-project/types': 0.127.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.127.0 + '@oxc-parser/binding-android-arm64': 0.127.0 + '@oxc-parser/binding-darwin-arm64': 0.127.0 + '@oxc-parser/binding-darwin-x64': 0.127.0 + '@oxc-parser/binding-freebsd-x64': 0.127.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.127.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.127.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.127.0 + '@oxc-parser/binding-linux-arm64-musl': 0.127.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.127.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.127.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.127.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.127.0 + '@oxc-parser/binding-linux-x64-gnu': 0.127.0 + '@oxc-parser/binding-linux-x64-musl': 0.127.0 + '@oxc-parser/binding-openharmony-arm64': 0.127.0 + '@oxc-parser/binding-wasm32-wasi': 0.127.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.127.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.127.0 + '@oxc-parser/binding-win32-x64-msvc': 0.127.0 + + oxc-resolver@11.21.2: + optionalDependencies: + '@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 + p-event@6.0.1: dependencies: p-timeout: 6.1.4 @@ -13946,6 +15081,8 @@ snapshots: pathe@2.0.3: {} + pathval@2.0.1: {} + peberminta@0.9.0: {} pend@1.2.0: {} @@ -14195,15 +15332,34 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-docgen-typescript@2.4.0(typescript@6.0.3): + dependencies: + typescript: 6.0.3 + + react-docgen@8.0.3(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/traverse': 7.29.8(supports-color@10.2.2) + '@babel/types': 7.29.8 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + '@types/doctrine': 0.0.9 + '@types/resolve': 1.20.6 + doctrine: 3.0.0 + resolve: 1.22.12 + strip-indent: 4.1.1 + transitivePeerDependencies: + - supports-color + react-dom@19.2.8(react@19.2.8): dependencies: react: 19.2.8 scheduler: 0.27.0 - react-email@6.9.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + react-email@6.9.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2): dependencies: '@babel/parser': 7.29.2 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@10.2.2) '@react-email/render': 2.1.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) chokidar: 4.0.3 commander: 13.1.0 @@ -14223,7 +15379,7 @@ snapshots: prompts: 2.4.2 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - socket.io: 4.8.3 + socket.io: 4.8.3(supports-color@10.2.2) tailwindcss: 4.3.3 tsconfig-paths: 4.2.0 transitivePeerDependencies: @@ -14235,17 +15391,17 @@ snapshots: react-is@17.0.2: {} - react-markdown@10.1.0(@types/react@19.2.18)(react@19.2.8): + react-markdown@10.1.0(@types/react@19.2.18)(react@19.2.8)(supports-color@10.2.2): dependencies: '@types/hast': 3.0.5 '@types/mdast': 4.0.4 '@types/react': 19.2.18 devlop: 1.1.0 - hast-util-to-jsx-runtime: 2.3.6 + hast-util-to-jsx-runtime: 2.3.6(supports-color@10.2.2) html-url-attributes: 3.0.1 mdast-util-to-hast: 13.2.1 react: 19.2.8 - remark-parse: 11.0.0 + remark-parse: 11.0.0(supports-color@10.2.2) remark-rehype: 11.1.2 unified: 11.0.5 unist-util-visit: 5.1.0 @@ -14319,6 +15475,14 @@ snapshots: real-require@1.0.0: {} + recast@0.23.21: + dependencies: + ast-types: 0.16.3 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -14352,21 +15516,21 @@ snapshots: dependencies: rc: 1.2.8 - remark-gfm@4.0.1: + remark-gfm@4.0.1(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 - mdast-util-gfm: 3.1.0 + mdast-util-gfm: 3.1.0(supports-color@10.2.2) micromark-extension-gfm: 3.0.0 - remark-parse: 11.0.0 + remark-parse: 11.0.0(supports-color@10.2.2) remark-stringify: 11.0.0 unified: 11.0.5 transitivePeerDependencies: - supports-color - remark-parse@11.0.0: + remark-parse@11.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) micromark-util-types: 2.0.2 unified: 11.0.5 transitivePeerDependencies: @@ -14416,6 +15580,13 @@ snapshots: resolve-pkg-maps@1.0.0: {} + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + resolve@2.0.0-next.7: dependencies: es-errors: 1.3.0 @@ -14465,7 +15636,7 @@ snapshots: rou3@0.9.2: {} - router@2.2.0: + router@2.2.0(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) depd: 2.0.0 @@ -14543,7 +15714,7 @@ snapshots: semver@7.8.5: {} - send@1.2.1: + send@1.2.1(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) encodeurl: 2.0.0 @@ -14559,12 +15730,12 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@2.2.1: + serve-static@2.2.1(supports-color@10.2.2): dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 1.2.1 + send: 1.2.1(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -14726,7 +15897,7 @@ snapshots: smol-toml@1.8.0: {} - socket.io-adapter@2.5.8: + socket.io-adapter@2.5.8(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) ws: 8.21.3 @@ -14735,22 +15906,22 @@ snapshots: - supports-color - utf-8-validate - socket.io-parser@4.2.7: + socket.io-parser@4.2.7(supports-color@10.2.2): dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color - socket.io@4.8.3: + socket.io@4.8.3(supports-color@10.2.2): dependencies: accepts: 1.3.8 base64id: 2.0.0 cors: 2.8.6 debug: 4.4.3(supports-color@10.2.2) - engine.io: 6.6.9 - socket.io-adapter: 2.5.8 - socket.io-parser: 4.2.7 + engine.io: 6.6.9(supports-color@10.2.2) + socket.io-adapter: 2.5.8(supports-color@10.2.2) + socket.io-parser: 4.2.7(supports-color@10.2.2) transitivePeerDependencies: - bufferutil - supports-color @@ -14791,6 +15962,8 @@ snapshots: split2@4.2.0: {} + sqids@0.3.0: {} + stable-hash@0.0.5: {} stack-generator@2.0.10: @@ -14819,6 +15992,33 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8): + dependencies: + '@storybook/global': 5.0.0 + '@storybook/icons': 2.1.0(react@19.2.8) + '@testing-library/dom': 10.4.1 + '@testing-library/jest-dom': 6.9.1 + '@testing-library/user-event': 14.6.6(@testing-library/dom@10.4.1) + '@vitest/expect': 3.2.4 + '@vitest/spy': 3.2.4 + '@webcontainer/env': 1.1.1 + esbuild: 0.28.1 + jsonc-parser: 3.3.1 + open: 10.2.0 + oxc-parser: 0.127.0 + oxc-resolver: 11.21.2 + recast: 0.23.21 + semver: 7.8.5 + use-sync-external-store: 1.6.0(react@19.2.8) + ws: 8.21.3 + optionalDependencies: + '@types/react': 19.2.18 + prettier: 3.9.6 + transitivePeerDependencies: + - bufferutil + - react + - utf-8-validate + streamx@2.28.0: dependencies: events-universal: 1.0.1 @@ -14933,6 +16133,8 @@ snapshots: dependencies: min-indent: 1.0.1 + strip-indent@4.1.1: {} + strip-json-comments@2.0.1: {} strip-json-comments@3.1.1: {} @@ -14951,12 +16153,12 @@ snapshots: dependencies: inline-style-parser: 0.2.7 - styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.8): + styled-jsx@5.1.6(@babel/core@7.29.7(supports-color@10.2.2))(react@19.2.8): dependencies: client-only: 0.0.1 react: 19.2.8 optionalDependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) supports-color@10.2.2: {} @@ -15050,6 +16252,8 @@ snapshots: through@2.3.8: {} + tiny-invariant@1.3.3: {} + tinybench@6.1.3: {} tinyclip@0.1.15: {} @@ -15061,8 +16265,12 @@ snapshots: fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 + tinyrainbow@2.0.0: {} + tinyrainbow@3.1.1: {} + tinyspy@4.0.4: {} + tldts-core@7.4.10: optional: true @@ -15071,6 +16279,16 @@ snapshots: tldts-core: 7.4.10 optional: true + tmcp@1.20.0(typescript@6.0.3): + dependencies: + '@standard-schema/spec': 1.1.0 + json-rpc-2.0: 1.8.0 + sqids: 0.3.0 + uri-template-matcher: 1.1.2 + valibot: 1.2.0(typescript@6.0.3) + transitivePeerDependencies: + - typescript + tmp-promise@3.0.3: dependencies: tmp: 0.2.7 @@ -15117,6 +16335,8 @@ snapshots: dependencies: typescript: 6.0.3 + ts-dedent@2.3.0: {} + ts-node@10.9.2(@types/node@24.13.3)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -15135,6 +16355,10 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + tsconfck@3.1.6(typescript@6.0.3): + optionalDependencies: + typescript: 6.0.3 + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -15203,13 +16427,13 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3): + typescript-eslint@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.67.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) - eslint: 9.39.5(jiti@2.7.0) + '@typescript-eslint/eslint-plugin': 8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -15295,6 +16519,13 @@ snapshots: unpipe@1.0.0: {} + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.18.0 + picomatch: 4.0.5 + webpack-virtual-modules: 0.6.2 + unrs-resolver@1.12.2: dependencies: napi-postinstall: 0.3.4 @@ -15322,7 +16553,7 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 - unstorage@1.17.5(@netlify/blobs@10.7.13): + unstorage@1.17.5(@netlify/blobs@10.7.13(supports-color@10.2.2)): dependencies: anymatch: 3.1.3 chokidar: 5.0.0 @@ -15364,14 +16595,24 @@ snapshots: dependencies: punycode: 2.3.1 + uri-template-matcher@1.1.2: {} + urlpattern-polyfill@10.1.0: {} urlpattern-polyfill@8.0.2: {} + use-sync-external-store@1.6.0(react@19.2.8): + dependencies: + react: 19.2.8 + util-deprecate@1.0.2: {} v8-compile-cache-lib@3.0.1: {} + valibot@1.2.0(typescript@6.0.3): + optionalDependencies: + typescript: 6.0.3 + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 @@ -15391,6 +16632,32 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 + vite-plugin-storybook-nextjs@3.3.2(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(supports-color@10.2.2)(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)): + dependencies: + '@next/env': 16.3.0-preview.5 + image-size: 2.0.2 + magic-string: 0.30.21 + module-alias: 2.3.4 + next: 16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + storybook: 10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8) + ts-dedent: 2.3.0 + vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + vite-tsconfig-paths: 5.1.4(supports-color@10.2.2)(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + transitivePeerDependencies: + - supports-color + - typescript + + vite-tsconfig-paths@5.1.4(supports-color@10.2.2)(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@6.0.3) + optionalDependencies: + vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + transitivePeerDependencies: + - supports-color + - typescript + vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0): dependencies: lightningcss: 1.33.0 @@ -15451,6 +16718,8 @@ snapshots: webidl-conversions@8.0.1: optional: true + webpack-virtual-modules@0.6.2: {} + whatwg-mimetype@5.0.0: optional: true @@ -15597,6 +16866,10 @@ snapshots: ws@8.21.3: {} + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.1 + wsl-utils@1.0.0: dependencies: is-wsl: 3.1.1 diff --git a/stories/Button.stories.ts b/stories/Button.stories.ts new file mode 100644 index 0000000..10e9d4b --- /dev/null +++ b/stories/Button.stories.ts @@ -0,0 +1,54 @@ +import type { Meta, StoryObj } from "@storybook/nextjs-vite"; + +import { fn } from "storybook/test"; + +import { Button } from "./Button"; + +// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export +const meta = { + title: "Example/Button", + component: Button, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout + layout: "centered", + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs + tags: ["autodocs"], + // More on argTypes: https://storybook.js.org/docs/api/arg-types + argTypes: { + backgroundColor: { control: "color" }, + }, + // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#story-args + args: { onClick: fn() }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Primary: Story = { + args: { + primary: true, + label: "Button", + }, +}; + +export const Secondary: Story = { + args: { + label: "Button", + }, +}; + +export const Large: Story = { + args: { + size: "large", + label: "Button", + }, +}; + +export const Small: Story = { + args: { + size: "small", + label: "Button", + }, +}; diff --git a/stories/Button.tsx b/stories/Button.tsx new file mode 100644 index 0000000..29e42f5 --- /dev/null +++ b/stories/Button.tsx @@ -0,0 +1,43 @@ +import "./button.css"; + +export interface ButtonProps { + /** Is this the principal call to action on the page? */ + primary?: boolean; + /** What background color to use */ + backgroundColor?: string; + /** How large should the button be? */ + size?: "small" | "medium" | "large"; + /** Button contents */ + label: string; + /** Optional click handler */ + onClick?: () => void; +} + +/** Primary UI component for user interaction */ +export const Button = ({ + primary = false, + size = "medium", + backgroundColor, + label, + ...props +}: ButtonProps) => { + const mode = primary + ? "storybook-button--primary" + : "storybook-button--secondary"; + return ( + + ); +}; diff --git a/stories/button.css b/stories/button.css new file mode 100644 index 0000000..04ffdf3 --- /dev/null +++ b/stories/button.css @@ -0,0 +1,30 @@ +.storybook-button { + display: inline-block; + cursor: pointer; + border: 0; + border-radius: 3em; + font-weight: 700; + line-height: 1; + font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.storybook-button--primary { + background-color: #555ab9; + color: white; +} +.storybook-button--secondary { + box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; + background-color: transparent; + color: #333; +} +.storybook-button--small { + padding: 10px 16px; + font-size: 12px; +} +.storybook-button--medium { + padding: 11px 20px; + font-size: 14px; +} +.storybook-button--large { + padding: 12px 24px; + font-size: 16px; +} diff --git a/vitest.config.mts b/vitest.config.mts index 881d099..403dd01 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -40,7 +40,9 @@ export default defineConfig({ include: ["**/*.test.ts"], exclude: [...defaultExclude, databaseIntegrationTestPattern], environment: "node", - sequence: { groupOrder: 0 }, + sequence: { + groupOrder: 0, + }, }, }, { @@ -55,7 +57,9 @@ export default defineConfig({ // sequential so each can reset the schema data before it runs. fileParallelism: false, isolate: true, - sequence: { groupOrder: 1 }, + sequence: { + groupOrder: 1, + }, }, }, { @@ -64,13 +68,22 @@ export default defineConfig({ name: "browser", include: ["**/*.integration.test.tsx"], setupFiles: ["./vitest.setup.ts"], - sequence: { groupOrder: 0 }, + sequence: { + groupOrder: 0, + }, browser: { enabled: true, headless: true, provider: playwright(), - instances: [{ browser: "chromium" }], - viewport: { width: 1280, height: 720 }, + instances: [ + { + browser: "chromium", + }, + ], + viewport: { + width: 1280, + height: 720, + }, }, }, }, diff --git a/vitest.shims.d.ts b/vitest.shims.d.ts new file mode 100644 index 0000000..03b1801 --- /dev/null +++ b/vitest.shims.d.ts @@ -0,0 +1 @@ +/// From f0ed47cd798499dc04777b21f2bf81ba44ef8835 Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Tue, 1 Sep 2026 16:26:45 -0700 Subject: [PATCH 2/6] chore: migrate to CFNext --- .storybook/main.ts | 8 +++----- .storybook/preview.tsx | 10 +++++----- stories/Button.stories.ts | 25 +++++++++++-------------- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/.storybook/main.ts b/.storybook/main.ts index 2617af8..b5ae066 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,9 +1,7 @@ -import type { StorybookConfig } from "@storybook/nextjs-vite"; - -const config: StorybookConfig = { +import { defineMain } from "@storybook/nextjs-vite/node"; +export default defineMain({ stories: ["../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)"], addons: ["@storybook/addon-mcp"], framework: "@storybook/nextjs-vite", staticDirs: ["../public"], -}; -export default config; +}); diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index c0adadc..3feec0d 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,7 +1,7 @@ -import type { Preview } from "@storybook/nextjs-vite"; +import { definePreview } from "@storybook/nextjs-vite"; -const preview: Preview = { - parameters: { +export default definePreview({ + parameters: { controls: { matchers: { color: /(background|color)$/i, @@ -9,6 +9,6 @@ const preview: Preview = { }, }, }, -}; -export default preview; + addons: [] +}); diff --git a/stories/Button.stories.ts b/stories/Button.stories.ts index 10e9d4b..670768c 100644 --- a/stories/Button.stories.ts +++ b/stories/Button.stories.ts @@ -1,11 +1,11 @@ -import type { Meta, StoryObj } from "@storybook/nextjs-vite"; +import preview from "../.storybook/preview"; import { fn } from "storybook/test"; import { Button } from "./Button"; // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export -const meta = { +const meta = preview.meta({ title: "Example/Button", component: Button, parameters: { @@ -20,35 +20,32 @@ const meta = { }, // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#story-args args: { onClick: fn() }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; +}); // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args -export const Primary: Story = { +export const Primary = meta.story({ args: { primary: true, label: "Button", }, -}; +}); -export const Secondary: Story = { +export const Secondary = meta.story({ args: { label: "Button", }, -}; +}); -export const Large: Story = { +export const Large = meta.story({ args: { size: "large", label: "Button", }, -}; +}); -export const Small: Story = { +export const Small = meta.story({ args: { size: "small", label: "Button", }, -}; +}); From 14b1ae618bb4bd70e4471f468718893dcc16845b Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Tue, 1 Sep 2026 16:48:09 -0700 Subject: [PATCH 3/6] chore: initial set of stories --- .gitignore | 1 + .prettierignore | 1 + .storybook/main.ts | 4 + .storybook/preview.tsx | 19 +- app/page.tsx | 12 +- eslint.config.mjs | 1 + package.json | 2 +- stories/Button.stories.ts | 51 ----- stories/Button.tsx | 43 ---- .../app/account/cancel-event-form.stories.tsx | 99 +++++++++ stories/app/account/page.stories.tsx | 57 +++++ .../app/account/sign-out-button.stories.tsx | 64 ++++++ .../admin/events/moderation-form.stories.tsx | 133 +++++++++++ stories/app/admin/events/page.stories.tsx | 87 ++++++++ .../users/admin-users-content.stories.tsx | 65 ++++++ stories/app/admin/users/page.stories.tsx | 84 +++++++ .../users/user-management-card.stories.tsx | 189 ++++++++++++++++ stories/app/auth/auth-form.stories.tsx | 210 ++++++++++++++++++ stories/app/auth/page.stories.tsx | 55 +++++ .../app/auth/reset-password/page.stories.tsx | 60 +++++ .../reset-password-form.stories.tsx | 208 +++++++++++++++++ .../app/auth/verify-email/page.stories.tsx | 65 ++++++ .../verification-result.stories.tsx | 71 ++++++ stories/app/code-of-conduct/page.stories.tsx | 12 + .../edit/collaborator-invite-form.stories.tsx | 92 ++++++++ .../events/[eventId]/edit/page.stories.tsx | 130 +++++++++++ .../components/calendar/calendar.stories.tsx | 76 +++++++ .../non-recurring-event-card.stories.tsx | 113 ++++++++++ .../recurring-event-card.stories.tsx | 85 +++++++ .../event-chip/event-chip.stories.tsx | 60 +++++ stories/app/events/events-page.stories.tsx | 63 ++++++ stories/app/events/page.stories.tsx | 100 +++++++++ .../event-description-editor.stories.tsx | 80 +++++++ .../app/events/submit/event-form.stories.tsx | 158 +++++++++++++ stories/app/events/submit/page.stories.tsx | 25 +++ stories/app/page.stories.tsx | 22 ++ stories/button.css | 30 --- stories/components/bridge-art.stories.tsx | 38 ++++ .../collapsible-event-description.stories.tsx | 79 +++++++ .../event-description-markdown.stories.tsx | 96 ++++++++ stories/components/join-community.stories.tsx | 79 +++++++ stories/components/site-footer.stories.tsx | 26 +++ stories/components/site-header.stories.tsx | 63 ++++++ stories/components/site-nav.stories.tsx | 84 +++++++ stories/fixtures/admin.ts | 171 ++++++++++++++ stories/fixtures/auth.ts | 46 ++++ stories/fixtures/events.ts | 148 ++++++++++++ stories/fixtures/managed-event.ts | 45 ++++ stories/fixtures/submissions.ts | 100 +++++++++ stories/story-coverage.test.ts | 42 ++++ tsconfig.json | 1 + 51 files changed, 3515 insertions(+), 130 deletions(-) delete mode 100644 stories/Button.stories.ts delete mode 100644 stories/Button.tsx create mode 100644 stories/app/account/cancel-event-form.stories.tsx create mode 100644 stories/app/account/page.stories.tsx create mode 100644 stories/app/account/sign-out-button.stories.tsx create mode 100644 stories/app/admin/events/moderation-form.stories.tsx create mode 100644 stories/app/admin/events/page.stories.tsx create mode 100644 stories/app/admin/users/admin-users-content.stories.tsx create mode 100644 stories/app/admin/users/page.stories.tsx create mode 100644 stories/app/admin/users/user-management-card.stories.tsx create mode 100644 stories/app/auth/auth-form.stories.tsx create mode 100644 stories/app/auth/page.stories.tsx create mode 100644 stories/app/auth/reset-password/page.stories.tsx create mode 100644 stories/app/auth/reset-password/reset-password-form.stories.tsx create mode 100644 stories/app/auth/verify-email/page.stories.tsx create mode 100644 stories/app/auth/verify-email/verification-result.stories.tsx create mode 100644 stories/app/code-of-conduct/page.stories.tsx create mode 100644 stories/app/events/[eventId]/edit/collaborator-invite-form.stories.tsx create mode 100644 stories/app/events/[eventId]/edit/page.stories.tsx create mode 100644 stories/app/events/components/calendar/calendar.stories.tsx create mode 100644 stories/app/events/components/event-cards/non-recurring-event-card.stories.tsx create mode 100644 stories/app/events/components/event-cards/recurring-event-card.stories.tsx create mode 100644 stories/app/events/components/event-chip/event-chip.stories.tsx create mode 100644 stories/app/events/events-page.stories.tsx create mode 100644 stories/app/events/page.stories.tsx create mode 100644 stories/app/events/submit/event-description-editor.stories.tsx create mode 100644 stories/app/events/submit/event-form.stories.tsx create mode 100644 stories/app/events/submit/page.stories.tsx create mode 100644 stories/app/page.stories.tsx delete mode 100644 stories/button.css create mode 100644 stories/components/bridge-art.stories.tsx create mode 100644 stories/components/collapsible-event-description.stories.tsx create mode 100644 stories/components/event-description-markdown.stories.tsx create mode 100644 stories/components/join-community.stories.tsx create mode 100644 stories/components/site-footer.stories.tsx create mode 100644 stories/components/site-header.stories.tsx create mode 100644 stories/components/site-nav.stories.tsx create mode 100644 stories/fixtures/admin.ts create mode 100644 stories/fixtures/auth.ts create mode 100644 stories/fixtures/events.ts create mode 100644 stories/fixtures/managed-event.ts create mode 100644 stories/fixtures/submissions.ts create mode 100644 stories/story-coverage.test.ts diff --git a/.gitignore b/.gitignore index 453e44a..29681aa 100644 --- a/.gitignore +++ b/.gitignore @@ -46,5 +46,6 @@ next-env.d.ts .vscode/ .idea/ +.pnpm-store/ *storybook.log storybook-static diff --git a/.prettierignore b/.prettierignore index 79a7a2e..dab86de 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,6 +7,7 @@ node_modules coverage out build +storybook-static next-env.d.ts *.tsbuildinfo diff --git a/.storybook/main.ts b/.storybook/main.ts index b5ae066..9097d78 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,7 +1,11 @@ import { defineMain } from "@storybook/nextjs-vite/node"; + export default defineMain({ stories: ["../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)"], addons: ["@storybook/addon-mcp"], framework: "@storybook/nextjs-vite", staticDirs: ["../public"], + features: { + experimentalRSC: true, + }, }); diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 3feec0d..2b434e6 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,14 +1,27 @@ import { definePreview } from "@storybook/nextjs-vite"; +import { sb } from "storybook/test"; +import "../app/globals.css"; + +sb.mock(import("../lib/session.ts")); +sb.mock(import("../lib/events/queries.ts")); +sb.mock(import("../lib/events/actions.ts")); +sb.mock(import("../lib/auth-client.ts")); +sb.mock(import("../lib/auth-config.ts")); +sb.mock(import("../lib/email-delivery.ts")); +sb.mock(import("../app/admin/users/actions.ts")); export default definePreview({ - parameters: { + addons: [], + parameters: { controls: { matchers: { color: /(background|color)$/i, date: /Date$/i, }, }, + layout: "fullscreen", + nextjs: { + appDirectory: true, + }, }, - - addons: [] }); diff --git a/app/page.tsx b/app/page.tsx index 327a601..a6f56e5 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -9,7 +9,11 @@ export const metadata: Metadata = { "Meet people, share what you know, and find technology events around Sacramento.", }; -export default function Home() { +interface HomePageProps { + inviteLink?: string; +} + +export function HomePage({ inviteLink }: HomePageProps) { return (
@@ -119,9 +123,13 @@ export default function Home() { in the local community.

- +
); } + +export default function Home() { + return ; +} diff --git a/eslint.config.mjs b/eslint.config.mjs index ce98339..589f1ba 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -14,6 +14,7 @@ export default defineConfig([ ".netlify/**", "out/**", "build/**", + "storybook-static/**", "next-env.d.ts", ]), eslintConfigPrettier, diff --git a/package.json b/package.json index ffdc44a..681846c 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "typecheck": "next typegen && tsc --noEmit", "test": "vitest run", "test:watch": "vitest", - "verify": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test && pnpm build", + "verify": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test && pnpm build-storybook && pnpm build", "db:generate": "drizzle-kit generate", "db:migrate": "netlify database migrations apply", "db:status": "netlify database status", diff --git a/stories/Button.stories.ts b/stories/Button.stories.ts deleted file mode 100644 index 670768c..0000000 --- a/stories/Button.stories.ts +++ /dev/null @@ -1,51 +0,0 @@ -import preview from "../.storybook/preview"; - -import { fn } from "storybook/test"; - -import { Button } from "./Button"; - -// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export -const meta = preview.meta({ - title: "Example/Button", - component: Button, - parameters: { - // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout - layout: "centered", - }, - // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs - tags: ["autodocs"], - // More on argTypes: https://storybook.js.org/docs/api/arg-types - argTypes: { - backgroundColor: { control: "color" }, - }, - // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#story-args - args: { onClick: fn() }, -}); - -// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args -export const Primary = meta.story({ - args: { - primary: true, - label: "Button", - }, -}); - -export const Secondary = meta.story({ - args: { - label: "Button", - }, -}); - -export const Large = meta.story({ - args: { - size: "large", - label: "Button", - }, -}); - -export const Small = meta.story({ - args: { - size: "small", - label: "Button", - }, -}); diff --git a/stories/Button.tsx b/stories/Button.tsx deleted file mode 100644 index 29e42f5..0000000 --- a/stories/Button.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import "./button.css"; - -export interface ButtonProps { - /** Is this the principal call to action on the page? */ - primary?: boolean; - /** What background color to use */ - backgroundColor?: string; - /** How large should the button be? */ - size?: "small" | "medium" | "large"; - /** Button contents */ - label: string; - /** Optional click handler */ - onClick?: () => void; -} - -/** Primary UI component for user interaction */ -export const Button = ({ - primary = false, - size = "medium", - backgroundColor, - label, - ...props -}: ButtonProps) => { - const mode = primary - ? "storybook-button--primary" - : "storybook-button--secondary"; - return ( - - ); -}; diff --git a/stories/app/account/cancel-event-form.stories.tsx b/stories/app/account/cancel-event-form.stories.tsx new file mode 100644 index 0000000..be838cc --- /dev/null +++ b/stories/app/account/cancel-event-form.stories.tsx @@ -0,0 +1,99 @@ +import preview from "@/.storybook/preview"; +import { CancelEventForm } from "@/app/account/cancel-event-form"; +import { cancelEvent } from "@/lib/events/actions"; +import { expect, mocked, spyOn } from "storybook/test"; + +const recurringArgs = { + defaultOccurrenceDate: "2026-10-22", + eventId: "storybook-event", + eventTitle: "Sacramento TypeScript Weekly", + isRecurring: true, + maxOccurrenceDate: "2027-02-25", + minOccurrenceDate: "2026-09-01", +}; + +const meta = preview.meta({ + component: CancelEventForm, + parameters: { + layout: "centered", + }, + args: recurringArgs, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +export const RecurringEvent = meta.story({ + beforeEach: () => { + mocked(cancelEvent).mockResolvedValue({ message: "", status: "idle" }); + }, +}); + +export const OneTimeEvent = RecurringEvent.extend({ + args: { + defaultOccurrenceDate: null, + isRecurring: false, + maxOccurrenceDate: null, + }, +}); + +export const NoFutureOccurrences = RecurringEvent.extend({ + args: { + defaultOccurrenceDate: null, + }, +}); + +export const OccurrenceCanceled = RecurringEvent.extend({ + beforeEach: () => { + spyOn(window, "confirm").mockReturnValue(true); + mocked(cancelEvent).mockResolvedValue({ + message: "Occurrence canceled. The rest of the series is unchanged.", + status: "success", + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Cancel this date" }), + ); + await expect(await canvas.findByRole("status")).toHaveTextContent( + "Occurrence canceled", + ); + }, +}); + +export const CancellationRejected = RecurringEvent.extend({ + beforeEach: () => { + spyOn(window, "confirm").mockReturnValue(true); + mocked(cancelEvent).mockResolvedValue({ + message: "That event occurrence is already canceled.", + status: "error", + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Cancel this date" }), + ); + await expect(await canvas.findByRole("alert")).toHaveTextContent( + "already canceled", + ); + }, +}); + +export const CancelingSeries = RecurringEvent.extend({ + beforeEach: () => { + spyOn(window, "confirm").mockReturnValue(true); + mocked(cancelEvent).mockImplementation(() => new Promise(() => {})); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Cancel the whole series" }), + ); + await expect( + canvas.getByRole("button", { name: "Canceling…" }), + ).toBeDisabled(); + }, +}); diff --git a/stories/app/account/page.stories.tsx b/stories/app/account/page.stories.tsx new file mode 100644 index 0000000..64fdded --- /dev/null +++ b/stories/app/account/page.stories.tsx @@ -0,0 +1,57 @@ +import preview from "@/.storybook/preview"; +import AccountPage from "@/app/account/page"; +import { getSubmissionsForUser } from "@/lib/events/queries"; +import { requireSession } from "@/lib/session"; +import { createSession } from "@/stories/fixtures/auth"; +import { mixedSubmissions } from "@/stories/fixtures/submissions"; +import { mocked } from "storybook/test"; + +const meta = preview.meta({ + component: AccountPage, + title: "Pages/Account", + parameters: { + layout: "fullscreen", + }, +}); + +export const NewSubmitter = meta.story({ + beforeEach: () => { + mocked(requireSession).mockResolvedValue(createSession()); + mocked(getSubmissionsForUser).mockResolvedValue([]); + }, +}); + +export const AdminWithManagedEvents = NewSubmitter.extend({ + beforeEach: () => { + mocked(requireSession).mockResolvedValue( + createSession({ + name: "Morgan Lee", + role: "admin", + }), + ); + mocked(getSubmissionsForUser).mockResolvedValue(mixedSubmissions); + }, +}); + +export const Approver = NewSubmitter.extend({ + beforeEach: () => { + mocked(requireSession).mockResolvedValue( + createSession({ + name: "Taylor Brooks", + role: "approver", + }), + ); + mocked(getSubmissionsForUser).mockResolvedValue( + mixedSubmissions.slice(0, 2), + ); + }, +}); + +export const Loading = NewSubmitter.extend({ + beforeEach: () => { + mocked(requireSession).mockImplementation(() => new Promise(() => {})); + mocked(getSubmissionsForUser).mockImplementation( + () => new Promise(() => {}), + ); + }, +}); diff --git a/stories/app/account/sign-out-button.stories.tsx b/stories/app/account/sign-out-button.stories.tsx new file mode 100644 index 0000000..7d431cf --- /dev/null +++ b/stories/app/account/sign-out-button.stories.tsx @@ -0,0 +1,64 @@ +import preview from "@/.storybook/preview"; +import { SignOutButton } from "@/app/account/sign-out-button"; +import { authClient } from "@/lib/auth-client"; +import { expect, mocked } from "storybook/test"; + +const meta = preview.meta({ + component: SignOutButton, + parameters: { + layout: "centered", + }, +}); + +export const Idle = meta.story({ + beforeEach: () => { + mocked(authClient.signOut).mockResolvedValue({ + data: null, + error: null, + }); + }, +}); + +export const ServiceError = Idle.extend({ + beforeEach: () => { + mocked(authClient.signOut).mockResolvedValue({ + data: null, + error: { + code: "SESSION_REVOKE_FAILED", + message: "The session could not be revoked.", + status: 500, + statusText: "Internal Server Error", + }, + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click(canvas.getByRole("button", { name: "Sign out" })); + await expect(await canvas.findByRole("status")).toHaveTextContent( + "session could not be revoked", + ); + }, +}); + +export const NetworkError = Idle.extend({ + beforeEach: () => { + mocked(authClient.signOut).mockRejectedValue(new Error("offline")); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click(canvas.getByRole("button", { name: "Sign out" })); + await expect(await canvas.findByRole("status")).toHaveTextContent( + "couldn't connect", + ); + }, +}); + +export const SigningOut = Idle.extend({ + beforeEach: () => { + mocked(authClient.signOut).mockImplementation(() => new Promise(() => {})); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click(canvas.getByRole("button", { name: "Sign out" })); + await expect( + canvas.getByRole("button", { name: "Signing out…" }), + ).toBeDisabled(); + }, +}); diff --git a/stories/app/admin/events/moderation-form.stories.tsx b/stories/app/admin/events/moderation-form.stories.tsx new file mode 100644 index 0000000..f039f9f --- /dev/null +++ b/stories/app/admin/events/moderation-form.stories.tsx @@ -0,0 +1,133 @@ +import preview from "@/.storybook/preview"; +import { ModerationForm } from "@/app/admin/events/moderation-form"; +import { moderateEvent, moderateEventEdit } from "@/lib/events/actions"; +import { expect, mocked } from "storybook/test"; + +const meta = preview.meta({ + title: "Admin/Events/Moderation Form", + component: ModerationForm, + parameters: { + layout: "centered", + }, + args: { + eventId: "9ae8c027-41d6-4890-a431-1d8208b22e40", + eventTitle: "Sacramento Community Demo Night", + reviewType: "event", + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +function mockIdleActions() { + mocked(moderateEvent).mockResolvedValue({ message: "", status: "idle" }); + mocked(moderateEventEdit).mockResolvedValue({ message: "", status: "idle" }); +} + +export const IdleEvent = meta.story({ + args: {}, + beforeEach: mockIdleActions, + play: async ({ canvas }) => { + await expect( + canvas.getByRole("button", { name: "Approve and publish" }), + ).toBeEnabled(); + await expect( + canvas.getByRole("button", { name: "Reject with note" }), + ).toBeEnabled(); + }, +}); + +export const IdleChangeRequest = meta.story({ + args: { + eventId: "08d3130a-2097-42a1-9319-a53cd149c625", + eventTitle: "Civic Tech Project Clinic", + reviewType: "edit", + }, + beforeEach: mockIdleActions, + play: async ({ canvas }) => { + await expect( + canvas.getByText( + "Approving replaces the live details with this proposed change.", + ), + ).toBeVisible(); + await expect( + canvas.getByRole("button", { name: "Approve changes" }), + ).toBeEnabled(); + }, +}); + +export const PendingDecision = meta.story({ + args: {}, + beforeEach: () => { + mockIdleActions(); + mocked(moderateEvent).mockImplementation(() => new Promise(() => {})); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Approve and publish" }), + ); + + for (const button of canvas.getAllByRole("button", { name: "Saving…" })) { + await expect(button).toBeDisabled(); + } + await expect( + canvas.getByLabelText("Note to submitter (optional)"), + ).toBeDisabled(); + }, +}); + +export const RejectionError = meta.story({ + args: {}, + beforeEach: () => { + mockIdleActions(); + mocked(moderateEvent).mockResolvedValue({ + message: + "Explain what the submitter needs to change before resubmitting.", + status: "error", + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.type( + canvas.getByLabelText("Note to submitter (optional)"), + "Needs work", + ); + await userEvent.click( + canvas.getByRole("button", { name: "Reject with note" }), + ); + + await expect(await canvas.findByRole("alert")).toHaveTextContent( + "Explain what the submitter needs to change", + ); + }, +}); + +export const Approved = meta.story({ + args: {}, + beforeEach: () => { + mockIdleActions(); + mocked(moderateEvent).mockResolvedValue({ + message: "Event approved and published.", + status: "success", + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.type( + canvas.getByLabelText("Note to submitter (optional)"), + "Ready for the calendar.", + ); + await userEvent.click( + canvas.getByRole("button", { name: "Approve and publish" }), + ); + + await expect(await canvas.findByRole("status")).toHaveTextContent( + "Event approved and published.", + ); + await expect( + canvas.getByLabelText("Note to submitter (optional)"), + ).toHaveValue(""); + }, +}); diff --git a/stories/app/admin/events/page.stories.tsx b/stories/app/admin/events/page.stories.tsx new file mode 100644 index 0000000..3ca1228 --- /dev/null +++ b/stories/app/admin/events/page.stories.tsx @@ -0,0 +1,87 @@ +import preview from "@/.storybook/preview"; +import AdminEventsPage from "@/app/admin/events/page"; +import { getPendingEventEdits, getPendingEvents } from "@/lib/events/queries"; +import { requireEventReviewerSession } from "@/lib/session"; +import { createSession } from "@/stories/fixtures/auth"; +import { + PENDING_ADMIN_EVENT_EDITS, + PENDING_ADMIN_EVENTS, +} from "@/stories/fixtures/admin"; +import { expect, mocked } from "storybook/test"; + +const meta = preview.meta({ + title: "Pages/Admin/Review Events", + component: AdminEventsPage, + parameters: { + layout: "fullscreen", + }, +}); + +function mockReviewerSession() { + mocked(requireEventReviewerSession).mockResolvedValue( + createSession({ + email: "reviewer@example.com", + id: "storybook-reviewer", + name: "Storybook Reviewer", + role: "approver", + }), + ); +} + +export const LoadingQueues = meta.story({ + beforeEach: () => { + mocked(requireEventReviewerSession).mockImplementation( + () => new Promise(() => {}), + ); + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { + name: "Loading submitted events…", + }), + ).toBeVisible(); + await expect( + canvas.getByRole("heading", { name: "Loading proposed changes…" }), + ).toBeVisible(); + }, +}); + +export const EmptyQueues = meta.story({ + beforeEach: () => { + mockReviewerSession(); + mocked(getPendingEvents).mockResolvedValue([]); + mocked(getPendingEventEdits).mockResolvedValue([]); + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { + name: "Nothing to review right now.", + }), + ).toBeVisible(); + await expect( + canvas.getByRole("heading", { + name: "No event changes are waiting.", + }), + ).toBeVisible(); + }, +}); + +export const PopulatedQueues = meta.story({ + beforeEach: () => { + mockReviewerSession(); + mocked(getPendingEvents).mockResolvedValue(PENDING_ADMIN_EVENTS); + mocked(getPendingEventEdits).mockResolvedValue(PENDING_ADMIN_EVENT_EDITS); + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { + name: "Sacramento Community Demo Night", + }), + ).toBeVisible(); + await expect( + canvas.getByRole("heading", { name: "Civic Tech Project Clinic" }), + ).toBeVisible(); + await expect(canvas.getByText("2 events waiting")).toBeVisible(); + await expect(canvas.getByText("1 change waiting")).toBeVisible(); + }, +}); diff --git a/stories/app/admin/users/admin-users-content.stories.tsx b/stories/app/admin/users/admin-users-content.stories.tsx new file mode 100644 index 0000000..c4f2bb8 --- /dev/null +++ b/stories/app/admin/users/admin-users-content.stories.tsx @@ -0,0 +1,65 @@ +import preview from "@/.storybook/preview"; +import { AdminUsersContent } from "@/app/admin/users/admin-users-content"; +import { auth } from "@/lib/auth"; +import { requireAdminSession } from "@/lib/session"; +import { + CURRENT_ADMIN_LISTED_USER, + OTHER_LISTED_USERS, +} from "@/stories/fixtures/admin"; +import { createSession } from "@/stories/fixtures/auth"; +import { expect, mocked } from "storybook/test"; + +const meta = preview.meta({ + title: "Admin/Users/User List", + component: AdminUsersContent, + parameters: { + layout: "padded", + }, +}); + +function mockAdminSession() { + mocked(requireAdminSession).mockResolvedValue( + createSession({ + email: CURRENT_ADMIN_LISTED_USER.email, + id: CURRENT_ADMIN_LISTED_USER.id, + name: CURRENT_ADMIN_LISTED_USER.name, + role: "admin", + }), + ); +} + +export const EmptyList = meta.story({ + beforeEach: () => { + mockAdminSession(); + mocked(auth.api.listUsers).mockResolvedValue({ + total: 1, + users: [CURRENT_ADMIN_LISTED_USER], + }); + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { name: "No other users yet." }), + ).toBeVisible(); + }, +}); + +export const PopulatedList = meta.story({ + beforeEach: () => { + mockAdminSession(); + mocked(auth.api.listUsers).mockResolvedValue({ + total: OTHER_LISTED_USERS.length + 1, + users: [CURRENT_ADMIN_LISTED_USER, ...OTHER_LISTED_USERS], + }); + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { name: "Alex Rivera" }), + ).toBeVisible(); + await expect( + canvas.getByRole("heading", { name: "Devon Brooks" }), + ).toBeVisible(); + await expect( + canvas.getByText("Banned", { selector: "span" }), + ).toBeVisible(); + }, +}); diff --git a/stories/app/admin/users/page.stories.tsx b/stories/app/admin/users/page.stories.tsx new file mode 100644 index 0000000..7227b51 --- /dev/null +++ b/stories/app/admin/users/page.stories.tsx @@ -0,0 +1,84 @@ +import preview from "@/.storybook/preview"; +import AdminUsersPage from "@/app/admin/users/page"; +import { auth } from "@/lib/auth"; +import { requireAdminSession } from "@/lib/session"; +import { + CURRENT_ADMIN_LISTED_USER, + OTHER_LISTED_USERS, +} from "@/stories/fixtures/admin"; +import { createSession } from "@/stories/fixtures/auth"; +import { expect, mocked } from "storybook/test"; + +const meta = preview.meta({ + title: "Pages/Admin/Manage Users", + component: AdminUsersPage, + parameters: { + layout: "fullscreen", + }, +}); + +function mockAdminSession() { + mocked(requireAdminSession).mockResolvedValue( + createSession({ + email: CURRENT_ADMIN_LISTED_USER.email, + id: CURRENT_ADMIN_LISTED_USER.id, + name: CURRENT_ADMIN_LISTED_USER.name, + role: "admin", + }), + ); +} + +export const LoadingUserList = meta.story({ + beforeEach: () => { + mocked(requireAdminSession).mockImplementation(() => new Promise(() => {})); + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { + name: "Loading community accounts…", + }), + ).toBeVisible(); + }, +}); + +export const EmptyUserList = meta.story({ + beforeEach: () => { + mockAdminSession(); + mocked(auth.api.listUsers).mockResolvedValue({ + total: 1, + users: [CURRENT_ADMIN_LISTED_USER], + }); + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { name: "No other users yet." }), + ).toBeVisible(); + await expect( + canvas.queryByRole("heading", { name: "Current Admin" }), + ).not.toBeInTheDocument(); + }, +}); + +export const PopulatedUserList = meta.story({ + beforeEach: () => { + mockAdminSession(); + mocked(auth.api.listUsers).mockResolvedValue({ + total: OTHER_LISTED_USERS.length + 1, + users: [CURRENT_ADMIN_LISTED_USER, ...OTHER_LISTED_USERS], + }); + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { name: "Alex Rivera" }), + ).toBeVisible(); + await expect( + canvas.getByRole("heading", { name: "Samira Patel" }), + ).toBeVisible(); + await expect( + canvas.getByRole("heading", { name: "Taylor Morgan" }), + ).toBeVisible(); + await expect( + canvas.queryByRole("heading", { name: "Current Admin" }), + ).not.toBeInTheDocument(); + }, +}); diff --git a/stories/app/admin/users/user-management-card.stories.tsx b/stories/app/admin/users/user-management-card.stories.tsx new file mode 100644 index 0000000..fc18a47 --- /dev/null +++ b/stories/app/admin/users/user-management-card.stories.tsx @@ -0,0 +1,189 @@ +import preview from "@/.storybook/preview"; +import { updateUserBan, updateUserRoles } from "@/app/admin/users/actions"; +import { UserManagementCard } from "@/app/admin/users/user-management-card"; +import { STORY_MANAGED_USERS } from "@/stories/fixtures/admin"; +import { expect, mocked } from "storybook/test"; + +const meta = preview.meta({ + title: "Admin/Users/User Management Card", + component: UserManagementCard, + parameters: { + layout: "centered", + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +function mockIdleActions() { + mocked(updateUserRoles).mockResolvedValue({ message: "", status: "idle" }); + mocked(updateUserBan).mockResolvedValue({ message: "", status: "idle" }); +} + +export const SubmitterActive = meta.story({ + args: { + user: STORY_MANAGED_USERS.submitter, + }, + beforeEach: mockIdleActions, +}); + +export const ApproverActive = meta.story({ + args: { + user: STORY_MANAGED_USERS.approver, + }, + beforeEach: mockIdleActions, +}); + +export const AdministratorActive = meta.story({ + args: { + user: STORY_MANAGED_USERS.administrator, + }, + beforeEach: mockIdleActions, +}); + +export const BannedSubmitter = meta.story({ + args: { + user: STORY_MANAGED_USERS.bannedSubmitter, + }, + beforeEach: mockIdleActions, + play: async ({ canvas }) => { + await expect( + canvas.getByText("Banned", { selector: "span" }), + ).toBeVisible(); + await expect( + canvas.getByRole("button", { name: "Unban account" }), + ).toBeEnabled(); + }, +}); + +export const SavingRoles = meta.story({ + args: { + user: STORY_MANAGED_USERS.submitter, + }, + beforeEach: () => { + mockIdleActions(); + mocked(updateUserRoles).mockImplementation(() => new Promise(() => {})); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click(canvas.getByRole("button", { name: "Save roles" })); + + await expect( + canvas.getByRole("button", { name: "Saving…" }), + ).toBeDisabled(); + await expect( + canvas.getByRole("button", { name: "Ban account" }), + ).toBeDisabled(); + }, +}); + +export const RoleUpdateError = meta.story({ + args: { + user: STORY_MANAGED_USERS.submitter, + }, + beforeEach: () => { + mockIdleActions(); + mocked(updateUserRoles).mockResolvedValue({ + message: "Choose at least one valid role.", + status: "error", + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click(canvas.getByRole("checkbox", { name: /Submitter/ })); + await userEvent.click(canvas.getByRole("button", { name: "Save roles" })); + + await expect(await canvas.findByRole("alert")).toHaveTextContent( + "Choose at least one valid role.", + ); + }, +}); + +export const RolesUpdated = meta.story({ + args: { + user: STORY_MANAGED_USERS.submitter, + }, + beforeEach: () => { + mockIdleActions(); + mocked(updateUserRoles).mockResolvedValue({ + message: "Roles updated.", + status: "success", + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click(canvas.getByRole("checkbox", { name: /Approver/ })); + await userEvent.click(canvas.getByRole("button", { name: "Save roles" })); + + await expect(await canvas.findByRole("status")).toHaveTextContent( + "Roles updated.", + ); + }, +}); + +export const UnbanningAccount = meta.story({ + args: { + user: STORY_MANAGED_USERS.bannedSubmitter, + }, + beforeEach: () => { + mockIdleActions(); + mocked(updateUserBan).mockImplementation(() => new Promise(() => {})); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Unban account" }), + ); + + await expect( + canvas.getByRole("button", { name: "Unbanning…" }), + ).toBeDisabled(); + await expect( + canvas.getByRole("button", { name: "Save roles" }), + ).toBeDisabled(); + }, +}); + +export const UnbanError = meta.story({ + args: { + user: STORY_MANAGED_USERS.bannedSubmitter, + }, + beforeEach: () => { + mockIdleActions(); + mocked(updateUserBan).mockResolvedValue({ + message: "The account could not be unbanned. Try again.", + status: "error", + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Unban account" }), + ); + + await expect(await canvas.findByRole("alert")).toHaveTextContent( + "The account could not be unbanned.", + ); + }, +}); + +export const AccountUnbanned = meta.story({ + args: { + user: STORY_MANAGED_USERS.bannedSubmitter, + }, + beforeEach: () => { + mockIdleActions(); + mocked(updateUserBan).mockResolvedValue({ + message: "Account unbanned.", + status: "success", + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Unban account" }), + ); + + await expect(await canvas.findByRole("status")).toHaveTextContent( + "Account unbanned.", + ); + }, +}); diff --git a/stories/app/auth/auth-form.stories.tsx b/stories/app/auth/auth-form.stories.tsx new file mode 100644 index 0000000..ef6a103 --- /dev/null +++ b/stories/app/auth/auth-form.stories.tsx @@ -0,0 +1,210 @@ +import preview from "@/.storybook/preview"; +import { AuthForm } from "@/app/auth/auth-form"; +import { authClient } from "@/lib/auth-client"; +import { expect, fireEvent, mocked } from "storybook/test"; + +const EMAIL = "person@example.com"; +const PASSWORD = "correct horse battery staple"; + +const meta = preview.meta({ + title: "Auth/Forms/Auth Form", + component: AuthForm, + parameters: { + layout: "centered", + }, + args: { + emailDeliveryEnabled: true, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +export const SignIn = meta.story({ + play: async ({ canvas }) => { + await expect( + canvas.getByRole("heading", { name: "Welcome back" }), + ).toBeVisible(); + await expect( + canvas.getByRole("button", { name: "Sign in", pressed: true }), + ).toBeVisible(); + }, +}); + +export const SignUp = SignIn.extend({ + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Create account" }), + ); + + await expect( + canvas.getByRole("heading", { name: "Create an account" }), + ).toBeVisible(); + await expect(canvas.getByRole("textbox", { name: "Name" })).toBeVisible(); + }, +}); + +export const ForgotPassword = SignIn.extend({ + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Forgot your password?" }), + ); + + await expect( + canvas.getByRole("heading", { name: "Reset your password" }), + ).toBeVisible(); + await expect(canvas.queryByLabelText("Password")).not.toBeInTheDocument(); + }, +}); + +export const EmailDeliveryDisabled = SignIn.extend({ + args: { + emailDeliveryEnabled: false, + }, + play: async ({ canvas }) => { + await expect( + canvas.queryByRole("button", { name: "Forgot your password?" }), + ).not.toBeInTheDocument(); + }, +}); + +export const ValidationError = SignIn.extend({ + play: async ({ canvas, canvasElement, userEvent }) => { + await userEvent.type( + canvas.getByRole("textbox", { name: "Email address" }), + EMAIL, + ); + await userEvent.type(canvas.getByLabelText("Password"), "too short"); + + fireEvent.submit(canvasElement.querySelector("form")!); + + await expect(await canvas.findByRole("alert")).toHaveTextContent( + "Your password must be at least 10 characters.", + ); + await expect(canvas.getByLabelText("Password")).toHaveAttribute( + "aria-invalid", + "true", + ); + }, +}); + +export const SigningIn = SignIn.extend({ + beforeEach: () => { + mocked(authClient.signIn.email).mockImplementation( + () => new Promise(() => {}), + ); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.type( + canvas.getByRole("textbox", { name: "Email address" }), + EMAIL, + ); + await userEvent.type(canvas.getByLabelText("Password"), PASSWORD); + await userEvent.click( + canvas.getAllByRole("button", { name: "Sign in" }).at(-1)!, + ); + + await expect( + canvas.getByRole("button", { name: "Signing in…" }), + ).toBeDisabled(); + }, +}); + +export const SignInServiceError = SignIn.extend({ + beforeEach: () => { + mocked(authClient.signIn.email).mockResolvedValue({ + data: null, + error: { + code: "INVALID_EMAIL_OR_PASSWORD", + message: "Invalid email or password", + status: 401, + statusText: "Unauthorized", + }, + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.type( + canvas.getByRole("textbox", { name: "Email address" }), + EMAIL, + ); + await userEvent.type(canvas.getByLabelText("Password"), PASSWORD); + await userEvent.click( + canvas.getAllByRole("button", { name: "Sign in" }).at(-1)!, + ); + + await expect(await canvas.findByRole("alert")).toHaveTextContent( + "That email and password don't match.", + ); + await expect( + canvas.getByRole("textbox", { name: "Email address" }), + ).toHaveValue(EMAIL); + }, +}); + +export const SignUpEmailSent = SignIn.extend({ + beforeEach: () => { + mocked(authClient.signUp.email).mockResolvedValue({ + data: null, + error: null, + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Create account" }), + ); + await userEvent.type( + canvas.getByRole("textbox", { name: "Name" }), + "Pat Lee", + ); + await userEvent.type( + canvas.getByRole("textbox", { name: "Email address" }), + EMAIL, + ); + await userEvent.type(canvas.getByLabelText("Password"), PASSWORD); + await userEvent.click( + canvas.getAllByRole("button", { name: "Create account" }).at(-1)!, + ); + + await expect( + await canvas.findByRole("heading", { name: "Check your email" }), + ).toBeVisible(); + await expect(canvas.getByRole("status")).toHaveTextContent( + "we'll send a verification link shortly", + ); + }, +}); + +export const PasswordResetEmailSent = SignIn.extend({ + beforeEach: () => { + mocked(authClient.requestPasswordReset).mockResolvedValue({ + data: null, + error: null, + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Forgot your password?" }), + ); + await userEvent.type( + canvas.getByRole("textbox", { name: "Email address" }), + EMAIL, + ); + await userEvent.click( + canvas.getByRole("button", { name: "Send reset link" }), + ); + + await expect(await canvas.findByRole("status")).toHaveTextContent( + "If an account exists for that address", + ); + }, +}); diff --git a/stories/app/auth/page.stories.tsx b/stories/app/auth/page.stories.tsx new file mode 100644 index 0000000..4bec5fa --- /dev/null +++ b/stories/app/auth/page.stories.tsx @@ -0,0 +1,55 @@ +import preview from "@/.storybook/preview"; +import AuthPage from "@/app/auth/page"; +import { isEmailDeliveryEnabled } from "@/lib/email-delivery"; +import { getCurrentSession } from "@/lib/session"; +import { expect, mocked } from "storybook/test"; + +const meta = preview.meta({ + title: "Pages/Auth/Sign In", + component: AuthPage, + parameters: { + layout: "fullscreen", + }, +}); + +export const SignedOut = meta.story({ + beforeEach: () => { + mocked(getCurrentSession).mockResolvedValue(null); + mocked(isEmailDeliveryEnabled).mockReturnValue(true); + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { name: "Share a local tech event." }), + ).toBeVisible(); + await expect( + canvas.getByRole("heading", { name: "Welcome back" }), + ).toBeVisible(); + }, +}); + +export const EmailDeliveryDisabled = SignedOut.extend({ + beforeEach: () => { + mocked(getCurrentSession).mockResolvedValue(null); + mocked(isEmailDeliveryEnabled).mockReturnValue(false); + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { name: "Welcome back" }), + ).toBeVisible(); + await expect( + canvas.queryByRole("button", { name: "Forgot your password?" }), + ).not.toBeInTheDocument(); + }, +}); + +export const Loading = SignedOut.extend({ + beforeEach: () => { + mocked(getCurrentSession).mockImplementation(() => new Promise(() => {})); + mocked(isEmailDeliveryEnabled).mockReturnValue(true); + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { name: "Checking your account" }), + ).toBeVisible(); + }, +}); diff --git a/stories/app/auth/reset-password/page.stories.tsx b/stories/app/auth/reset-password/page.stories.tsx new file mode 100644 index 0000000..0ef4889 --- /dev/null +++ b/stories/app/auth/reset-password/page.stories.tsx @@ -0,0 +1,60 @@ +import preview from "@/.storybook/preview"; +import ResetPasswordPage from "@/app/auth/reset-password/page"; +import { expect } from "storybook/test"; + +const meta = preview.meta({ + title: "Pages/Auth/Reset Password", + component: ResetPasswordPage, + parameters: { + controls: { disable: true }, + layout: "fullscreen", + }, + args: { + searchParams: Promise.resolve({ token: "storybook-reset-token" }), + }, +}); + +export const ValidLink = meta.story({ + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { name: "Choose a new password." }), + ).toBeVisible(); + await expect( + canvas.getByRole("heading", { name: "Reset your password" }), + ).toBeVisible(); + }, +}); + +export const MissingToken = ValidLink.extend({ + args: { + searchParams: Promise.resolve({}), + }, + play: async ({ canvas }) => { + await expect(await canvas.findByText(/missing its token/i)).toBeVisible(); + }, +}); + +export const InvalidLink = ValidLink.extend({ + args: { + searchParams: Promise.resolve({ + error: "INVALID_TOKEN", + token: "storybook-reset-token", + }), + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByText(/invalid or has expired/i), + ).toBeVisible(); + }, +}); + +export const Loading = ValidLink.extend({ + args: { + searchParams: new Promise(() => {}), + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { name: "Checking your reset link" }), + ).toBeVisible(); + }, +}); diff --git a/stories/app/auth/reset-password/reset-password-form.stories.tsx b/stories/app/auth/reset-password/reset-password-form.stories.tsx new file mode 100644 index 0000000..1cecc5e --- /dev/null +++ b/stories/app/auth/reset-password/reset-password-form.stories.tsx @@ -0,0 +1,208 @@ +import preview from "@/.storybook/preview"; +import { ResetPasswordForm } from "@/app/auth/reset-password/reset-password-form"; +import { authClient } from "@/lib/auth-client"; +import { expect, mocked } from "storybook/test"; + +const TOKEN = "storybook-reset-token"; +const PASSWORD = "correct horse battery staple"; + +const meta = preview.meta({ + title: "Auth/Forms/Reset Password Form", + component: ResetPasswordForm, + parameters: { + layout: "centered", + }, + args: { + error: null, + token: TOKEN, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +export const ValidToken = meta.story({ + play: async ({ canvas }) => { + await expect( + canvas.getByRole("heading", { name: "Reset your password" }), + ).toBeVisible(); + await expect(canvas.getByLabelText("New password")).toBeEnabled(); + }, +}); + +export const MissingToken = ValidToken.extend({ + args: { + token: null, + }, + play: async ({ canvas }) => { + await expect(canvas.getByText(/missing its token/i)).toBeVisible(); + await expect( + canvas.queryByLabelText("New password"), + ).not.toBeInTheDocument(); + }, +}); + +export const InvalidToken = ValidToken.extend({ + args: { + error: "INVALID_TOKEN", + }, + play: async ({ canvas }) => { + await expect(canvas.getByText(/invalid or has expired/i)).toBeVisible(); + await expect( + canvas.queryByLabelText("New password"), + ).not.toBeInTheDocument(); + }, +}); + +export const PasswordTooShort = ValidToken.extend({ + play: async ({ canvas, userEvent }) => { + await userEvent.type(canvas.getByLabelText("New password"), "too short"); + await userEvent.type( + canvas.getByLabelText("Confirm new password"), + "too short", + ); + await userEvent.click( + canvas.getByRole("button", { name: "Reset password" }), + ); + + await expect(await canvas.findByRole("alert")).toHaveTextContent( + "Your password must be at least 10 characters.", + ); + }, +}); + +export const PasswordsDoNotMatch = ValidToken.extend({ + play: async ({ canvas, userEvent }) => { + await userEvent.type(canvas.getByLabelText("New password"), PASSWORD); + await userEvent.type( + canvas.getByLabelText("Confirm new password"), + "a different secure password", + ); + await userEvent.click( + canvas.getByRole("button", { name: "Reset password" }), + ); + + await expect(await canvas.findByRole("alert")).toHaveTextContent( + "The passwords don't match.", + ); + }, +}); + +export const ResettingPassword = ValidToken.extend({ + beforeEach: () => { + mocked(authClient.resetPassword).mockImplementation( + () => new Promise(() => {}), + ); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.type(canvas.getByLabelText("New password"), PASSWORD); + await userEvent.type( + canvas.getByLabelText("Confirm new password"), + PASSWORD, + ); + await userEvent.click( + canvas.getByRole("button", { name: "Reset password" }), + ); + + await expect( + canvas.getByRole("button", { name: "Resetting password…" }), + ).toBeDisabled(); + }, +}); + +export const PasswordReset = ValidToken.extend({ + beforeEach: () => { + mocked(authClient.resetPassword).mockResolvedValue({ + data: null, + error: null, + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.type(canvas.getByLabelText("New password"), PASSWORD); + await userEvent.type( + canvas.getByLabelText("Confirm new password"), + PASSWORD, + ); + await userEvent.click( + canvas.getByRole("button", { name: "Reset password" }), + ); + + await expect( + await canvas.findByRole("heading", { + name: "Your password has been reset", + }), + ).toBeVisible(); + await expect( + canvas.getByRole("link", { name: /Back to sign in/ }), + ).toHaveAttribute("href", "/auth"); + }, +}); + +export const ServiceError = ValidToken.extend({ + beforeEach: () => { + mocked(authClient.resetPassword).mockResolvedValue({ + data: null, + error: { + code: "SERVICE_UNAVAILABLE", + message: "We couldn't reset your password. Try again.", + status: 503, + statusText: "Service Unavailable", + }, + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.type(canvas.getByLabelText("New password"), PASSWORD); + await userEvent.type( + canvas.getByLabelText("Confirm new password"), + PASSWORD, + ); + await userEvent.click( + canvas.getByRole("button", { name: "Reset password" }), + ); + + await expect(await canvas.findByRole("alert")).toHaveTextContent( + "We couldn't reset your password. Try again.", + ); + await expect(canvas.getByLabelText("New password")).toHaveValue(PASSWORD); + }, +}); + +export const ServiceRejectsToken = ValidToken.extend({ + beforeEach: () => { + mocked(authClient.resetPassword).mockResolvedValue({ + data: null, + error: { + code: "TOKEN_EXPIRED", + message: "The reset token expired.", + status: 400, + statusText: "Bad Request", + }, + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.type(canvas.getByLabelText("New password"), PASSWORD); + await userEvent.type( + canvas.getByLabelText("Confirm new password"), + PASSWORD, + ); + await userEvent.click( + canvas.getByRole("button", { name: "Reset password" }), + ); + + await expect( + await canvas.findByRole("heading", { + name: "Request a new reset link", + }), + ).toBeVisible(); + }, +}); diff --git a/stories/app/auth/verify-email/page.stories.tsx b/stories/app/auth/verify-email/page.stories.tsx new file mode 100644 index 0000000..d2d3fcd --- /dev/null +++ b/stories/app/auth/verify-email/page.stories.tsx @@ -0,0 +1,65 @@ +import preview from "@/.storybook/preview"; +import VerifyEmailPage from "@/app/auth/verify-email/page"; +import { expect } from "storybook/test"; + +const meta = preview.meta({ + title: "Pages/Auth/Verify Email", + component: VerifyEmailPage, + parameters: { + controls: { disable: true }, + layout: "fullscreen", + }, + args: { + searchParams: Promise.resolve({ error: "TOKEN_EXPIRED" }), + }, +}); + +export const Expired = meta.story({ + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { name: "Verify your email." }), + ).toBeVisible(); + await expect( + canvas.getByRole("heading", { name: "Verification link expired" }), + ).toBeVisible(); + }, +}); + +export const Invalid = Expired.extend({ + args: { + searchParams: Promise.resolve({ error: "INVALID_TOKEN" }), + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { + name: "Verification link unavailable", + }), + ).toBeVisible(); + }, +}); + +export const GenericFailure = Expired.extend({ + args: { + searchParams: Promise.resolve({ error: "USER_NOT_FOUND" }), + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { + name: "We couldn't verify your email", + }), + ).toBeVisible(); + }, +}); + +export const Loading = Expired.extend({ + args: { + searchParams: new Promise(() => {}), + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { + name: "Checking your verification link", + }), + ).toBeVisible(); + }, +}); diff --git a/stories/app/auth/verify-email/verification-result.stories.tsx b/stories/app/auth/verify-email/verification-result.stories.tsx new file mode 100644 index 0000000..3194499 --- /dev/null +++ b/stories/app/auth/verify-email/verification-result.stories.tsx @@ -0,0 +1,71 @@ +import preview from "@/.storybook/preview"; +import { VerifyEmailResult } from "@/app/auth/verify-email/verification-result"; +import { expect } from "storybook/test"; + +const meta = preview.meta({ + title: "Auth/Results/Email Verification", + component: VerifyEmailResult, + parameters: { + controls: { disable: true }, + layout: "centered", + }, + args: { + searchParams: Promise.resolve({ error: "TOKEN_EXPIRED" }), + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +export const Expired = meta.story({ + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { + name: "Verification link expired", + }), + ).toBeVisible(); + await expect(canvas.getByRole("alert")).toHaveTextContent("has expired"); + }, +}); + +export const Invalid = Expired.extend({ + args: { + searchParams: Promise.resolve({ error: "INVALID_TOKEN" }), + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { + name: "Verification link unavailable", + }), + ).toBeVisible(); + await expect(canvas.getByRole("alert")).toHaveTextContent( + "invalid or has already been used", + ); + }, +}); + +export const GenericFailure = Expired.extend({ + args: { + searchParams: Promise.resolve({ error: "USER_NOT_FOUND" }), + }, + play: async ({ canvas }) => { + await expect( + await canvas.findByRole("heading", { + name: "We couldn't verify your email", + }), + ).toBeVisible(); + await expect( + canvas.getByRole("link", { name: /Back to sign in/ }), + ).toHaveAttribute("href", "/auth"); + }, +}); diff --git a/stories/app/code-of-conduct/page.stories.tsx b/stories/app/code-of-conduct/page.stories.tsx new file mode 100644 index 0000000..2de5512 --- /dev/null +++ b/stories/app/code-of-conduct/page.stories.tsx @@ -0,0 +1,12 @@ +import preview from "@/.storybook/preview"; +import CodeOfConductPage from "@/app/code-of-conduct/page"; + +const meta = preview.meta({ + component: CodeOfConductPage, + title: "Pages/Code of Conduct", + parameters: { + layout: "fullscreen", + }, +}); + +export const FullPolicy = meta.story({}); diff --git a/stories/app/events/[eventId]/edit/collaborator-invite-form.stories.tsx b/stories/app/events/[eventId]/edit/collaborator-invite-form.stories.tsx new file mode 100644 index 0000000..109ad0f --- /dev/null +++ b/stories/app/events/[eventId]/edit/collaborator-invite-form.stories.tsx @@ -0,0 +1,92 @@ +import preview from "@/.storybook/preview"; +import { CollaboratorInviteForm } from "@/app/events/[eventId]/edit/collaborator-invite-form"; +import { inviteEventCollaborator } from "@/lib/events/actions"; +import { expect, mocked } from "storybook/test"; + +const meta = preview.meta({ + component: CollaboratorInviteForm, + parameters: { + layout: "centered", + }, + args: { + eventId: "storybook-event", + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +export const Idle = meta.story({ + beforeEach: () => { + mocked(inviteEventCollaborator).mockResolvedValue({ + message: "", + status: "idle", + }); + }, +}); + +export const InvitationSent = Idle.extend({ + beforeEach: () => { + mocked(inviteEventCollaborator).mockResolvedValue({ + message: "Editor invitation sent to teammate@example.com.", + status: "success", + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.type( + canvas.getByRole("textbox", { name: "Account email" }), + "teammate@example.com", + ); + await userEvent.click( + canvas.getByRole("button", { name: "Invite editor" }), + ); + await expect(await canvas.findByRole("status")).toHaveTextContent( + "invitation sent", + ); + }, +}); + +export const AccountNotFound = Idle.extend({ + beforeEach: () => { + mocked(inviteEventCollaborator).mockResolvedValue({ + message: "No SacTech account uses that email address.", + status: "error", + }); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.type( + canvas.getByRole("textbox", { name: "Account email" }), + "missing@example.com", + ); + await userEvent.click( + canvas.getByRole("button", { name: "Invite editor" }), + ); + await expect(await canvas.findByRole("alert")).toHaveTextContent( + "No SacTech account", + ); + }, +}); + +export const Inviting = Idle.extend({ + beforeEach: () => { + mocked(inviteEventCollaborator).mockImplementation( + () => new Promise(() => {}), + ); + }, + play: async ({ canvas, userEvent }) => { + await userEvent.type( + canvas.getByRole("textbox", { name: "Account email" }), + "teammate@example.com", + ); + await userEvent.click( + canvas.getByRole("button", { name: "Invite editor" }), + ); + await expect( + canvas.getByRole("button", { name: "Inviting…" }), + ).toBeDisabled(); + }, +}); diff --git a/stories/app/events/[eventId]/edit/page.stories.tsx b/stories/app/events/[eventId]/edit/page.stories.tsx new file mode 100644 index 0000000..5759c07 --- /dev/null +++ b/stories/app/events/[eventId]/edit/page.stories.tsx @@ -0,0 +1,130 @@ +import preview from "@/.storybook/preview"; +import EditEventPage from "@/app/events/[eventId]/edit/page"; +import { getManagedEventForEdit } from "@/lib/events/queries"; +import { requireSession } from "@/lib/session"; +import { createSession } from "@/stories/fixtures/auth"; +import { createManagedEvent } from "@/stories/fixtures/managed-event"; +import { mocked } from "storybook/test"; + +const seriesArgs = { + params: Promise.resolve({ eventId: "storybook-managed-event" }), + searchParams: Promise.resolve({ scope: "series" }), +}; + +const meta = preview.meta({ + component: EditEventPage, + title: "Pages/Events/Edit", + parameters: { + controls: { disable: true }, + layout: "fullscreen", + }, + args: seriesArgs, +}); + +export const WholeSeries = meta.story({ + beforeEach: () => { + mocked(requireSession).mockResolvedValue(createSession()); + mocked(getManagedEventForEdit).mockResolvedValue(createManagedEvent()); + }, +}); + +export const OneOccurrence = WholeSeries.extend({ + args: { + searchParams: Promise.resolve({ + occurrenceDate: "2026-10-22", + scope: "occurrence", + }), + }, + beforeEach: () => { + mocked(requireSession).mockResolvedValue(createSession()); + mocked(getManagedEventForEdit).mockResolvedValue( + createManagedEvent({ + occurrenceOverride: { + description: + "A special hands-on edition with extra time for guided practice.", + endsAt: new Date("2026-10-23T04:00:00.000Z"), + eventUrl: "https://example.com/typescript-workshop", + locationAddress: "123 J Street, Sacramento, CA 95814", + locationName: "The Urban Hive", + mode: "in_person", + occurrenceDate: "2026-10-22", + startsAt: new Date("2026-10-23T01:00:00.000Z"), + timezone: "America/Los_Angeles", + title: "TypeScript Hands-on Night", + }, + }), + ); + }, +}); + +export const ChangesPending = WholeSeries.extend({ + beforeEach: () => { + mocked(requireSession).mockResolvedValue(createSession()); + mocked(getManagedEventForEdit).mockResolvedValue( + createManagedEvent({ + changeRequests: [ + { + createdAt: new Date("2026-08-30T18:00:00.000Z"), + moderationNote: null, + occurrenceDate: null, + scope: "series", + status: "pending", + }, + ], + }), + ); + }, +}); + +export const ReviewerRequestedChanges = WholeSeries.extend({ + beforeEach: () => { + mocked(requireSession).mockResolvedValue(createSession()); + mocked(getManagedEventForEdit).mockResolvedValue( + createManagedEvent({ + changeRequests: [ + { + createdAt: new Date("2026-08-29T18:00:00.000Z"), + moderationNote: + "Please add the accessibility details for the venue before resubmitting.", + occurrenceDate: null, + scope: "series", + status: "rejected", + }, + ], + }), + ); + }, +}); + +export const CanceledEvent = WholeSeries.extend({ + beforeEach: () => { + mocked(requireSession).mockResolvedValue(createSession()); + mocked(getManagedEventForEdit).mockResolvedValue( + createManagedEvent({ + canceledAt: new Date("2026-08-28T18:00:00.000Z"), + }), + ); + }, +}); + +export const SharedWithCurrentUser = WholeSeries.extend({ + beforeEach: () => { + mocked(requireSession).mockResolvedValue(createSession()); + mocked(getManagedEventForEdit).mockResolvedValue( + createManagedEvent({ + collaborators: [], + isOwner: false, + submittedBy: "another-user", + }), + ); + }, +}); + +export const Loading = WholeSeries.extend({ + beforeEach: () => { + mocked(requireSession).mockImplementation(() => new Promise(() => {})); + mocked(getManagedEventForEdit).mockImplementation( + () => new Promise(() => {}), + ); + }, +}); diff --git a/stories/app/events/components/calendar/calendar.stories.tsx b/stories/app/events/components/calendar/calendar.stories.tsx new file mode 100644 index 0000000..ebc8f8d --- /dev/null +++ b/stories/app/events/components/calendar/calendar.stories.tsx @@ -0,0 +1,76 @@ +import preview from "../../../../../.storybook/preview"; + +import { expect } from "storybook/test"; + +import { Calendar } from "@/app/events/components/calendar/calendar"; +import { + createRecurringEvent, + createRecurringEventWithOverride, + createSpecialEvent, + EVENT_STORY_REFERENCE_DATE, + MIXED_EVENTS, +} from "@/stories/fixtures/events"; + +const meta = preview.meta({ + title: "Events/Components/Calendar", + component: Calendar, + parameters: { + layout: "padded", + }, + args: { + events: MIXED_EVENTS, + referenceDate: EVENT_STORY_REFERENCE_DATE, + }, + argTypes: { + events: { control: false }, + }, +}); + +export const MixedEvents = meta.story({}); + +export const Empty = MixedEvents.extend({ + args: { + events: [], + }, +}); + +export const SelectedOccurrence = MixedEvents.extend({ + args: { + events: [createRecurringEventWithOverride()], + }, + play: async ({ canvas, userEvent }) => { + const occurrence = canvas.getByRole("button", { + name: "September 15, 2026, 1 event", + }); + + await userEvent.click(occurrence); + + await expect(occurrence).toHaveAttribute("aria-pressed", "true"); + await expect( + canvas.getByRole("region", { name: "September 15, 2026" }), + ).toBeVisible(); + await expect(canvas.getByText("TypeScript Hands-on Night")).toBeVisible(); + }, +}); + +export const NextMonthWindow = meta.story({ + args: { + events: [createRecurringEvent(), createSpecialEvent()], + }, + play: async ({ canvas, userEvent }) => { + await expect( + canvas.getByRole("table", { name: "September 2026" }), + ).toBeVisible(); + + await userEvent.click( + canvas.getByRole("button", { name: "Show next month" }), + ); + + await expect( + canvas.queryByRole("table", { name: "September 2026" }), + ).not.toBeInTheDocument(); + await expect( + canvas.getByRole("table", { name: "December 2026" }), + ).toBeVisible(); + }, +}); diff --git a/stories/app/events/components/event-cards/non-recurring-event-card.stories.tsx b/stories/app/events/components/event-cards/non-recurring-event-card.stories.tsx new file mode 100644 index 0000000..aa2edcc --- /dev/null +++ b/stories/app/events/components/event-cards/non-recurring-event-card.stories.tsx @@ -0,0 +1,113 @@ +import preview from "../../../../../.storybook/preview"; + +import { expect } from "storybook/test"; + +import { NonRecurringEventsCard } from "@/app/events/components/event-cards/non-recurring-event-card"; +import { + createEventBlock, + createLongEventDescription, + createSpecialEvent, +} from "@/stories/fixtures/events"; + +const meta = preview.meta({ + title: "Events/Components/Event Cards/Non-recurring", + component: NonRecurringEventsCard, + parameters: { + layout: "padded", + }, + args: { + event: createSpecialEvent(), + }, + argTypes: { + event: { control: false }, + }, + render: (args) => ( +
    + +
+ ), +}); + +export const InPerson = meta.story({}); + +export const Online = InPerson.extend({ + args: { + event: createSpecialEvent({ + blocks: [ + createEventBlock({ + in_person: false, + is_online: true, + location_address: undefined, + location_description: "Online", + location_url: "https://events.example.com/online-design-lab", + slug: "online-design-lab-occurrence", + title: "Online Design Lab", + }), + ], + in_person: false, + is_online: true, + location_address: undefined, + location_description: "Online", + location_url: "https://events.example.com/online-design-lab", + slug: "online-design-lab", + title: "Online Design Lab", + }), + }, +}); + +export const HybridMultiDay = InPerson.extend({ + args: { + event: createSpecialEvent({ + blocks: [ + createEventBlock({ + ends_at: new Date("2026-10-05T23:00:00.000Z"), + in_person: true, + is_online: true, + location_description: "Sacramento Convention Center + livestream", + location_url: "https://events.example.com/civic-tech-weekend", + slug: "civic-tech-weekend-occurrence", + starts_at: new Date("2026-10-03T16:00:00.000Z"), + title: "Civic Tech Weekend", + }), + ], + in_person: true, + is_online: true, + location_description: "Sacramento Convention Center + livestream", + location_url: "https://events.example.com/civic-tech-weekend", + slug: "civic-tech-weekend", + title: "Civic Tech Weekend", + }), + }, +}); + +export const DatesComingSoon = InPerson.extend({ + args: { + event: createSpecialEvent({ + blocks: [], + location_url: undefined, + slug: "future-community-showcase", + title: "Future Community Showcase", + }), + }, +}); + +const expandedDescriptionTail = "The final workshop details are now visible."; + +export const ExpandedDescription = InPerson.extend({ + args: { + event: createSpecialEvent({ + description: createLongEventDescription(expandedDescriptionTail), + }), + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { + name: "Show more details for Sacramento Design Summit", + }), + ); + + await expect( + canvas.getByText(expandedDescriptionTail, { exact: false }), + ).toBeVisible(); + }, +}); diff --git a/stories/app/events/components/event-cards/recurring-event-card.stories.tsx b/stories/app/events/components/event-cards/recurring-event-card.stories.tsx new file mode 100644 index 0000000..c3f8b94 --- /dev/null +++ b/stories/app/events/components/event-cards/recurring-event-card.stories.tsx @@ -0,0 +1,85 @@ +import preview from "../../../../../.storybook/preview"; + +import { expect } from "storybook/test"; + +import { RecurringEventsCard } from "@/app/events/components/event-cards/recurring-event-card"; +import { + createLongEventDescription, + createRecurringEvent, + createRecurringEventWithOverride, + createWeeklyRecurrenceRule, + EVENT_STORY_REFERENCE_DATE, +} from "@/stories/fixtures/events"; + +const meta = preview.meta({ + title: "Events/Components/Event Cards/Recurring", + component: RecurringEventsCard, + parameters: { + layout: "padded", + }, + args: { + event: createRecurringEvent(), + referenceDate: EVENT_STORY_REFERENCE_DATE, + }, + argTypes: { + event: { control: false }, + }, + render: (args) => ( +
    + +
+ ), +}); + +export const WeeklyOnline = meta.story({}); + +export const OccurrenceOverride = WeeklyOnline.extend({ + args: { + event: createRecurringEventWithOverride(), + referenceDate: "2026-09-09", + }, + play: async ({ canvas }) => { + await expect( + canvas.getByRole("heading", { name: "TypeScript Hands-on Night" }), + ).toBeVisible(); + await expect(canvas.getByText("In person")).toBeVisible(); + await expect(canvas.queryByText("Online")).not.toBeInTheDocument(); + await expect( + canvas.getByRole("link", { + name: "The Urban Hive: TypeScript Hands-on Night", + }), + ).toHaveAttribute("href", "https://events.example.com/hands-on-night"); + }, +}); + +export const NoUpcomingDates = WeeklyOnline.extend({ + args: { + event: createRecurringEvent({ + recurrence_rule: createWeeklyRecurrenceRule({ + excludedDates: ["2026-09-02", "2026-09-09"], + occurrenceCount: 2, + }), + }), + }, +}); + +const expandedDescriptionTail = "The complete recurring agenda is visible."; + +export const ExpandedDescription = WeeklyOnline.extend({ + args: { + event: createRecurringEvent({ + description: createLongEventDescription(expandedDescriptionTail), + }), + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { + name: "Show more details for Sacramento TypeScript Weekly", + }), + ); + + await expect( + canvas.getByText(expandedDescriptionTail, { exact: false }), + ).toBeVisible(); + }, +}); diff --git a/stories/app/events/components/event-chip/event-chip.stories.tsx b/stories/app/events/components/event-chip/event-chip.stories.tsx new file mode 100644 index 0000000..db29bf2 --- /dev/null +++ b/stories/app/events/components/event-chip/event-chip.stories.tsx @@ -0,0 +1,60 @@ +import preview from "../../../../../.storybook/preview"; + +import { EventChip } from "@/app/events/components/event-chip/event-chip"; + +const meta = preview.meta({ + title: "Events/Components/Event Chip", + component: EventChip, + parameters: { + layout: "centered", + }, + args: { + size: "default", + variant: "online", + }, +}); + +export const Online = meta.story({ + args: { + size: "default", + variant: "online", + }, +}); + +export const OnlineCompact = Online.extend({ + args: { + size: "compact", + }, +}); + +export const InPerson = Online.extend({ + args: { + variant: "in-person", + }, +}); + +export const RecurringDaily = meta.story({ + args: { + every: "day", + size: "default", + variant: "recurring", + }, +}); + +export const RecurringWeekly = RecurringDaily.extend({ + args: { + every: "week", + }, +}); + +export const RecurringMonthly = RecurringDaily.extend({ + args: { + every: "month", + }, +}); + +export const RecurringYearly = RecurringDaily.extend({ + args: { + every: "year", + }, +}); diff --git a/stories/app/events/events-page.stories.tsx b/stories/app/events/events-page.stories.tsx new file mode 100644 index 0000000..239f44f --- /dev/null +++ b/stories/app/events/events-page.stories.tsx @@ -0,0 +1,63 @@ +import preview from "../../../.storybook/preview"; + +import { expect } from "storybook/test"; + +import EventsPage, { EventsCallouts } from "@/app/events/events-page"; +import { + EVENT_STORY_REFERENCE_DATE, + MIXED_EVENTS, +} from "@/stories/fixtures/events"; + +const meta = preview.meta({ + title: "Events/Pages/Events Page", + component: EventsPage, + parameters: { + layout: "padded", + }, + args: { + events: MIXED_EVENTS, + referenceDate: EVENT_STORY_REFERENCE_DATE, + }, + argTypes: { + events: { control: false }, + }, +}); + +export const MixedSchedule = meta.story({}); + +export const EmptySchedule = MixedSchedule.extend({ + args: { + events: [], + }, +}); + +export const FilteredToOnline = MixedSchedule.extend({ + play: async ({ canvas, userEvent }) => { + await userEvent.click(canvas.getByRole("radio", { name: "Online" })); + + await expect(canvas.getByRole("radio", { name: "Online" })).toBeChecked(); + await expect(canvas.getByRole("status")).toHaveTextContent( + "Showing 1 event for online.", + ); + await expect( + canvas.getByRole("heading", { + name: "Sacramento TypeScript Weekly", + }), + ).toBeVisible(); + await expect( + canvas.queryByRole("heading", { name: "Sacramento Design Summit" }), + ).not.toBeInTheDocument(); + }, +}); + +export const CallsToAction = meta.story({ + render: () => , + play: async ({ canvas }) => { + await expect( + canvas.getByRole("link", { name: "Join the community" }), + ).toHaveAttribute("href", "/#join"); + await expect( + canvas.getByRole("link", { name: "Submit an event" }), + ).toHaveAttribute("href", "/account"); + }, +}); diff --git a/stories/app/events/page.stories.tsx b/stories/app/events/page.stories.tsx new file mode 100644 index 0000000..a53ca38 --- /dev/null +++ b/stories/app/events/page.stories.tsx @@ -0,0 +1,100 @@ +import preview from "@/.storybook/preview"; +import EventsPage, { EventsCallouts } from "@/app/events/events-page"; +import style from "@/app/events/events-page.module.css"; +import type { Event } from "@/app/events/types"; +import { BridgeArt } from "@/components/bridge-art"; +import { + EVENT_STORY_REFERENCE_DATE, + MIXED_EVENTS, +} from "@/stories/fixtures/events"; + +interface EventsRoutePreviewProps { + events: Event[]; + referenceDate: string; +} + +function EventsRoutePreview({ + events, + referenceDate, +}: EventsRoutePreviewProps) { + return ( +
+
+
+
+

Gather by the river

+

Find your next local tech event.

+

+ Meet people across the region who design, build, teach, and learn + about technology. Browse what's scheduled now. +

+
+ +
+
+ +
+ + +
+
+ ); +} + +const meta = preview.meta({ + component: EventsRoutePreview, + title: "Pages/Events", + parameters: { + layout: "fullscreen", + }, + args: { + events: MIXED_EVENTS, + referenceDate: EVENT_STORY_REFERENCE_DATE, + }, +}); + +export const PopulatedCalendar = meta.story({}); + +export const EmptyCalendar = PopulatedCalendar.extend({ + args: { + events: [], + }, +}); + +export const LoadingCalendar = PopulatedCalendar.extend({ + render: () => ( +
+
+
+
+

Gather by the river

+

Find your next local tech event.

+
+ +
+
+
+
+
+
+

Community calendar

+

See what's coming up

+
+
+
+
+

Loading schedule

+

Gathering the latest event details.

+

The community calendar will be ready in a moment.

+
+
+
+ +
+
+ ), +}); diff --git a/stories/app/events/submit/event-description-editor.stories.tsx b/stories/app/events/submit/event-description-editor.stories.tsx new file mode 100644 index 0000000..166f49d --- /dev/null +++ b/stories/app/events/submit/event-description-editor.stories.tsx @@ -0,0 +1,80 @@ +import preview from "@/.storybook/preview"; +import { EventDescriptionEditor } from "@/app/events/submit/event-description-editor"; +import { fn } from "storybook/test"; + +const formattedDescription = `## What to expect + +Bring a laptop and meet other Sacramento developers. + +- Short project demos +- Friendly code review +- Time to meet collaborators + +> First-time attendees are especially welcome. + +Read the [community guidelines](/code-of-conduct) before joining. + +\`\`\`ts +const greeting = "Hello, Sacramento!"; +\`\`\``; + +const meta = preview.meta({ + component: EventDescriptionEditor, + parameters: { + layout: "padded", + }, + args: { + "aria-describedby": "description-help", + disabled: false, + onChange: fn(), + value: "", + }, + decorators: [ + (Story) => ( +
+ +

+ Tell people what will happen and who the event is for. +

+ +
+ ), + ], +}); + +export const Empty = meta.story({}); + +export const Formatted = Empty.extend({ + args: { + value: formattedDescription, + }, +}); + +export const Invalid = Formatted.extend({ + args: { + "aria-invalid": true, + }, +}); + +export const Disabled = Formatted.extend({ + args: { + disabled: true, + }, +}); + +export const OverCharacterLimit = Empty.extend({ + args: { + value: "A".repeat(4_025), + }, +}); + +export const BoldText = Empty.extend({ + play: async ({ canvas, userEvent }) => { + const editor = canvas.getByRole("textbox", { name: "Description" }); + await userEvent.click(editor); + await userEvent.click(canvas.getByRole("button", { name: "Bold" })); + await userEvent.type(editor, "A hands-on community workshop"); + }, +}); diff --git a/stories/app/events/submit/event-form.stories.tsx b/stories/app/events/submit/event-form.stories.tsx new file mode 100644 index 0000000..bdd80c0 --- /dev/null +++ b/stories/app/events/submit/event-form.stories.tsx @@ -0,0 +1,158 @@ +import preview from "@/.storybook/preview"; +import { + EventForm, + type EventFormAction, + type EventFormValues, +} from "@/app/events/submit/event-form"; +import type { EventFormState } from "@/lib/events/state"; +import { expect, fn } from "storybook/test"; + +const idleAction = fn(async () => ({ + message: "", + status: "idle", +})); + +const baseValues: EventFormValues = { + description: + "A welcoming demo night for Sacramento developers, designers, and makers.", + endsAt: "2026-10-15T20:30", + eventUrl: "https://example.com/sacramento-demo-night", + locationAddress: "828 I Street, Sacramento, CA 95814", + locationName: "Sacramento Central Library", + mode: "hybrid", + recurrenceCount: "8", + recurrenceEndDate: "2027-02-18", + recurrenceEndType: "never", + recurrenceFrequency: "week", + recurrenceInterval: "1", + recurrenceMonthlyPattern: "day_of_month", + recurrenceWeekdays: [4], + recurring: false, + startsAt: "2026-10-15T18:00", + title: "Sacramento Community Demo Night", +}; + +const weeklyValues: EventFormValues = { + ...baseValues, + recurrenceEndType: "after_occurrences", + recurrenceWeekdays: [2, 4], + recurring: true, +}; + +const meta = preview.meta({ + component: EventForm, + parameters: { + layout: "padded", + }, + args: { + action: idleAction, + allowRecurrence: true, + variant: "submit", + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +export const BlankSubmission = meta.story({}); + +export const OneTimeHybridEvent = BlankSubmission.extend({ + args: { + initialValues: baseValues, + }, +}); + +export const WeeklySeries = OneTimeHybridEvent.extend({ + args: { + initialValues: weeklyValues, + }, +}); + +export const MonthlySeries = WeeklySeries.extend({ + args: { + initialValues: { + ...weeklyValues, + recurrenceEndDate: "2027-10-15", + recurrenceEndType: "on_date", + recurrenceFrequency: "month", + recurrenceInterval: "1", + recurrenceMonthlyPattern: "nth_weekday", + }, + }, +}); + +export const YearlySeries = WeeklySeries.extend({ + args: { + initialValues: { + ...weeklyValues, + recurrenceEndType: "never", + recurrenceFrequency: "year", + recurrenceInterval: "1", + }, + }, +}); + +export const OccurrenceEdit = OneTimeHybridEvent.extend({ + args: { + allowRecurrence: false, + variant: "edit", + }, +}); + +const validationError: EventFormState = { + errors: { + eventUrl: ["Use a complete http:// or https:// link."], + title: ["Use a more descriptive event title."], + }, + message: "Review the highlighted fields and try again.", + status: "error", +}; + +export const ServerValidationErrors = OneTimeHybridEvent.extend({ + args: { + action: fn(async () => validationError), + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Submit event for review" }), + ); + await expect(await canvas.findByRole("alert")).toHaveTextContent( + "Review the highlighted fields", + ); + }, +}); + +export const Submitted = OneTimeHybridEvent.extend({ + args: { + action: fn(async () => ({ + message: "Event submitted for review.", + status: "success", + })), + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Submit event for review" }), + ); + await expect(await canvas.findByRole("status")).toHaveTextContent( + "Event submitted for review.", + ); + }, +}); + +export const Submitting = OneTimeHybridEvent.extend({ + args: { + action: fn(() => new Promise(() => {})), + }, + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: "Submit event for review" }), + ); + await expect( + canvas.getByRole("button", { name: "Submitting…" }), + ).toBeDisabled(); + }, +}); diff --git a/stories/app/events/submit/page.stories.tsx b/stories/app/events/submit/page.stories.tsx new file mode 100644 index 0000000..611d41f --- /dev/null +++ b/stories/app/events/submit/page.stories.tsx @@ -0,0 +1,25 @@ +import preview from "@/.storybook/preview"; +import SubmitEventPage from "@/app/events/submit/page"; +import { requireSession } from "@/lib/session"; +import { createSession } from "@/stories/fixtures/auth"; +import { mocked } from "storybook/test"; + +const meta = preview.meta({ + component: SubmitEventPage, + title: "Pages/Events/Submit", + parameters: { + layout: "fullscreen", + }, +}); + +export const Authenticated = meta.story({ + beforeEach: () => { + mocked(requireSession).mockResolvedValue(createSession()); + }, +}); + +export const CheckingSession = Authenticated.extend({ + beforeEach: () => { + mocked(requireSession).mockImplementation(() => new Promise(() => {})); + }, +}); diff --git a/stories/app/page.stories.tsx b/stories/app/page.stories.tsx new file mode 100644 index 0000000..bd74998 --- /dev/null +++ b/stories/app/page.stories.tsx @@ -0,0 +1,22 @@ +import preview from "@/.storybook/preview"; +import { HomePage } from "@/app/page"; + +const meta = preview.meta({ + component: HomePage, + title: "Pages/Home", + parameters: { + layout: "fullscreen", + }, +}); + +export const SlackInviteAvailable = meta.story({ + args: { + inviteLink: "https://join.slack.com/t/sactech/shared_invite/storybook", + }, +}); + +export const SlackInviteUnavailable = SlackInviteAvailable.extend({ + args: { + inviteLink: undefined, + }, +}); diff --git a/stories/button.css b/stories/button.css deleted file mode 100644 index 04ffdf3..0000000 --- a/stories/button.css +++ /dev/null @@ -1,30 +0,0 @@ -.storybook-button { - display: inline-block; - cursor: pointer; - border: 0; - border-radius: 3em; - font-weight: 700; - line-height: 1; - font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; -} -.storybook-button--primary { - background-color: #555ab9; - color: white; -} -.storybook-button--secondary { - box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; - background-color: transparent; - color: #333; -} -.storybook-button--small { - padding: 10px 16px; - font-size: 12px; -} -.storybook-button--medium { - padding: 11px 20px; - font-size: 14px; -} -.storybook-button--large { - padding: 12px 24px; - font-size: 16px; -} diff --git a/stories/components/bridge-art.stories.tsx b/stories/components/bridge-art.stories.tsx new file mode 100644 index 0000000..816451f --- /dev/null +++ b/stories/components/bridge-art.stories.tsx @@ -0,0 +1,38 @@ +import preview from "@/.storybook/preview"; +import { BridgeArt } from "@/components/bridge-art"; +import { expect } from "storybook/test"; + +const meta = preview.meta({ + title: "Components/Bridge Art", + component: BridgeArt, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +export const Full = meta.story({ + args: { + compact: false, + }, + play: async ({ canvasElement }) => { + const artwork = canvasElement.querySelector("[aria-hidden='true']"); + const image = canvasElement.querySelector("img"); + + await expect(artwork).toHaveAttribute("aria-hidden", "true"); + await expect(image).toBeInTheDocument(); + }, +}); + +export const Compact = Full.extend({ + args: { + compact: true, + }, +}); diff --git a/stories/components/collapsible-event-description.stories.tsx b/stories/components/collapsible-event-description.stories.tsx new file mode 100644 index 0000000..8a2e1cf --- /dev/null +++ b/stories/components/collapsible-event-description.stories.tsx @@ -0,0 +1,79 @@ +import preview from "@/.storybook/preview"; +import { CollapsibleEventDescription } from "@/components/collapsible-event-description"; +import { expect } from "storybook/test"; + +const longMarkdown = `${"A".repeat(295)} **bold tail stays formatted**\n\n[Event details](https://events.example.com/details)`; + +const meta = preview.meta({ + title: "Components/Collapsible Event Description", + component: CollapsibleEventDescription, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +export const ExactlyAtLimit = meta.story({ + args: { + eventTitle: "A concise event", + markdown: "A".repeat(300), + }, + play: async ({ canvas }) => { + await expect(canvas.getByText("A".repeat(300))).toBeVisible(); + await expect(canvas.queryByRole("button")).not.toBeInTheDocument(); + }, +}); + +export const LongDescription = meta.story({ + args: { + eventTitle: "SacTech community night", + markdown: longMarkdown, + }, + play: async ({ canvas }) => { + const toggle = canvas.getByRole("button", { + name: "Show more details for SacTech community night", + }); + + await expect(toggle).toHaveAttribute("aria-expanded", "false"); + await expect(canvas.getByText("bold…").tagName).toBe("STRONG"); + await expect( + canvas.queryByRole("link", { name: "Event details" }), + ).not.toBeInTheDocument(); + }, +}); + +export const Expanded = LongDescription.extend({ + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { + name: "Show more details for SacTech community night", + }), + ); + + await expect( + canvas.getByRole("button", { + name: "Show less details for SacTech community night", + }), + ).toHaveAttribute("aria-expanded", "true"); + await expect(canvas.getByText("bold tail stays formatted").tagName).toBe( + "STRONG", + ); + await expect( + canvas.getByRole("link", { name: "Event details" }), + ).toBeVisible(); + }, +}); diff --git a/stories/components/event-description-markdown.stories.tsx b/stories/components/event-description-markdown.stories.tsx new file mode 100644 index 0000000..b3a15d1 --- /dev/null +++ b/stories/components/event-description-markdown.stories.tsx @@ -0,0 +1,96 @@ +import preview from "@/.storybook/preview"; +import { EventDescriptionMarkdown } from "@/components/event-description-markdown"; +import { expect } from "storybook/test"; + +const richMarkdown = [ + "# What to expect", + "", + "Meet **Sacramento builders** and share what you are learning.", + "", + "> Bring a project, a question, or just your curiosity.", + "", + "- See community demos", + "- Meet a collaborator", + "- Try `pnpm test` with a neighbor", + "", + "```ts", + 'const greeting = "Hello, Sacramento!";', + "```", + "", + "[Event details](https://events.example.com/sac-tech)", +].join("\n"); + +const meta = preview.meta({ + title: "Components/Event Description Markdown", + component: EventDescriptionMarkdown, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +export const RichContent = meta.story({ + args: { + markdown: richMarkdown, + }, + play: async ({ canvas }) => { + await expect( + canvas.getByRole("heading", { level: 4, name: "What to expect" }), + ).toBeVisible(); + await expect(canvas.getByText("Sacramento builders").tagName).toBe( + "STRONG", + ); + await expect(canvas.getAllByRole("listitem")).toHaveLength(3); + await expect( + canvas.getByRole("link", { name: "Event details" }), + ).toHaveAttribute("target", "_blank"); + }, +}); + +export const TruncatedContent = meta.story({ + args: { + markdown: `${"A".repeat(295)} **bold ending remains valid**\n\n[Hidden link](https://example.com)`, + maxCharacters: 300, + }, + play: async ({ canvas }) => { + await expect(canvas.getByText("bold…").tagName).toBe("STRONG"); + await expect( + canvas.queryByRole("link", { name: "Hidden link" }), + ).not.toBeInTheDocument(); + }, +}); + +export const UnsafeInput = meta.story({ + args: { + markdown: [ + "Safe introduction.", + "", + "", + '', + "", + "[Unsafe destination](javascript:alert('nope'))", + ].join("\n"), + }, + play: async ({ canvas, canvasElement }) => { + await expect(canvas.getByText("Safe introduction.")).toBeVisible(); + await expect(canvasElement.querySelector("script")).not.toBeInTheDocument(); + await expect(canvasElement.querySelector("img")).not.toBeInTheDocument(); + await expect( + canvas.getByText("Unsafe destination").closest("a"), + ).not.toHaveAttribute("href"); + }, +}); diff --git a/stories/components/join-community.stories.tsx b/stories/components/join-community.stories.tsx new file mode 100644 index 0000000..d5c3f00 --- /dev/null +++ b/stories/components/join-community.stories.tsx @@ -0,0 +1,79 @@ +import preview from "@/.storybook/preview"; +import { JoinCommunity } from "@/components/join-community"; +import { expect } from "storybook/test"; + +const meta = preview.meta({ + title: "Components/Join Community", + component: JoinCommunity, + parameters: { + layout: "centered", + nextjs: { + appDirectory: true, + }, + }, + tags: ["autodocs"], + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +export const InviteUnavailable = meta.story({ + args: {}, + play: async ({ canvas }) => { + await expect( + canvas.getByText("We're updating the Slack invitations."), + ).toBeVisible(); + await expect( + canvas.getByRole("link", { name: "Code of Conduct" }), + ).toHaveAttribute("href", "/code-of-conduct"); + }, +}); + +export const InviteAvailable = meta.story({ + args: { + inviteLink: "https://community.example.com/sactech", + }, + play: async ({ canvas }) => { + await expect( + canvas.getByRole("checkbox", { name: /I agree to the Code of Conduct/ }), + ).not.toBeChecked(); + await expect( + canvas.getByRole("button", { name: /Join SacTech Slack/ }), + ).toBeEnabled(); + }, +}); + +export const AgreementRequired = InviteAvailable.extend({ + play: async ({ canvas, userEvent }) => { + await userEvent.click( + canvas.getByRole("button", { name: /Join SacTech Slack/ }), + ); + + await expect(canvas.getByRole("status")).toHaveTextContent( + "Please agree to the Code of Conduct before joining.", + ); + }, +}); + +export const AgreementSelected = InviteAvailable.extend({ + play: async ({ canvas, userEvent }) => { + const agreement = canvas.getByRole("checkbox", { + name: /I agree to the Code of Conduct/, + }); + + await userEvent.click(agreement); + await expect(agreement).toBeChecked(); + await expect(canvas.queryByRole("status")).not.toBeInTheDocument(); + }, +}); diff --git a/stories/components/site-footer.stories.tsx b/stories/components/site-footer.stories.tsx new file mode 100644 index 0000000..1c86998 --- /dev/null +++ b/stories/components/site-footer.stories.tsx @@ -0,0 +1,26 @@ +import preview from "@/.storybook/preview"; +import { SiteFooter } from "@/components/site-footer"; +import { expect } from "storybook/test"; + +const meta = preview.meta({ + title: "Components/Site Footer", + component: SiteFooter, + parameters: { + layout: "fullscreen", + nextjs: { + appDirectory: true, + }, + }, + tags: ["autodocs"], +}); + +export const Default = meta.story({ + play: async ({ canvas }) => { + await expect(canvas.getByRole("contentinfo")).toBeVisible(); + await expect(canvas.getByAltText("SacTech")).toBeVisible(); + await expect(canvas.getAllByRole("link")).toHaveLength(3); + await expect( + canvas.getByRole("navigation", { name: "Footer" }), + ).toBeVisible(); + }, +}); diff --git a/stories/components/site-header.stories.tsx b/stories/components/site-header.stories.tsx new file mode 100644 index 0000000..bc9ac0d --- /dev/null +++ b/stories/components/site-header.stories.tsx @@ -0,0 +1,63 @@ +import preview from "@/.storybook/preview"; +import { SiteHeader } from "@/components/site-header"; +import { expect } from "storybook/test"; + +const meta = preview.meta({ + title: "Components/Site Header", + component: SiteHeader, + parameters: { + layout: "fullscreen", + nextjs: { + appDirectory: true, + navigation: { + pathname: "/", + }, + }, + }, + tags: ["autodocs"], +}); + +export const HomeRoute = meta.story({ + play: async ({ canvas }) => { + await expect( + canvas.getByRole("banner").querySelector("img"), + ).toBeInTheDocument(); + await expect( + canvas.getByRole("link", { name: "Community" }), + ).toHaveAttribute("aria-current", "page"); + await expect( + canvas.getByRole("link", { name: "Join the community" }), + ).toHaveAttribute("href", "/#join"); + }, +}); + +export const EventsRoute = HomeRoute.extend({ + parameters: { + nextjs: { + navigation: { + pathname: "/events", + }, + }, + }, + play: async ({ canvas }) => { + await expect(canvas.getByRole("link", { name: "Events" })).toHaveAttribute( + "aria-current", + "page", + ); + }, +}); + +export const CodeOfConductRoute = HomeRoute.extend({ + parameters: { + nextjs: { + navigation: { + pathname: "/code-of-conduct", + }, + }, + }, + play: async ({ canvas }) => { + await expect( + canvas.getByRole("link", { name: "Code of Conduct" }), + ).toHaveAttribute("aria-current", "page"); + }, +}); diff --git a/stories/components/site-nav.stories.tsx b/stories/components/site-nav.stories.tsx new file mode 100644 index 0000000..34203c0 --- /dev/null +++ b/stories/components/site-nav.stories.tsx @@ -0,0 +1,84 @@ +import preview from "@/.storybook/preview"; +import { SiteNav } from "@/components/site-nav"; +import { expect } from "storybook/test"; + +const meta = preview.meta({ + title: "Components/Site Navigation", + component: SiteNav, + parameters: { + layout: "centered", + nextjs: { + appDirectory: true, + navigation: { + pathname: "/", + }, + }, + }, + tags: ["autodocs"], + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}); + +export const HomeRoute = meta.story({ + play: async ({ canvas }) => { + await expect( + canvas.getByRole("link", { name: "Community" }), + ).toHaveAttribute("aria-current", "page"); + }, +}); + +export const EventsRoute = HomeRoute.extend({ + parameters: { + nextjs: { + navigation: { + pathname: "/events", + }, + }, + }, + play: async ({ canvas }) => { + await expect(canvas.getByRole("link", { name: "Events" })).toHaveAttribute( + "aria-current", + "page", + ); + }, +}); + +export const CodeOfConductRoute = HomeRoute.extend({ + parameters: { + nextjs: { + navigation: { + pathname: "/code-of-conduct", + }, + }, + }, + play: async ({ canvas }) => { + await expect( + canvas.getByRole("link", { name: "Code of Conduct" }), + ).toHaveAttribute("aria-current", "page"); + }, +}); + +export const UnmatchedRoute = HomeRoute.extend({ + parameters: { + nextjs: { + navigation: { + pathname: "/account", + }, + }, + }, + play: async ({ canvas }) => { + for (const link of canvas.getAllByRole("link")) { + await expect(link).not.toHaveAttribute("aria-current"); + } + }, +}); diff --git a/stories/fixtures/admin.ts b/stories/fixtures/admin.ts new file mode 100644 index 0000000..b1e5019 --- /dev/null +++ b/stories/fixtures/admin.ts @@ -0,0 +1,171 @@ +import type { ManagedUser } from "@/app/admin/users/user-management-card"; +import type { + getPendingEventEdits, + getPendingEvents, +} from "@/lib/events/queries"; + +type PendingEvents = Awaited>; +type PendingEventEdits = Awaited>; + +export const PENDING_ADMIN_EVENTS = [ + { + canceledOccurrences: [], + createdAt: new Date("2026-09-02T17:30:00.000Z"), + description: + "A welcoming evening of short demos from Sacramento developers, designers, and makers.", + endsAt: new Date("2026-10-16T03:30:00.000Z"), + eventUrl: "https://example.com/sacramento-demo-night", + id: "9ae8c027-41d6-4890-a431-1d8208b22e40", + locationAddress: "828 I Street, Sacramento, CA 95814", + locationName: "Sacramento Central Library", + mode: "hybrid", + recurrenceCount: null, + recurrenceEndDate: null, + recurrenceEndType: null, + recurrenceFrequency: null, + recurrenceInterval: null, + recurrenceMonthlyPattern: null, + recurrenceWeekdays: null, + startsAt: new Date("2026-10-16T01:00:00.000Z"), + submitterEmail: "maya.chen@example.com", + submitterName: "Maya Chen", + timezone: "America/Los_Angeles", + title: "Sacramento Community Demo Night", + }, + { + canceledOccurrences: ["2026-11-03"], + createdAt: new Date("2026-09-04T20:15:00.000Z"), + description: + "A practical weekly TypeScript study group with a rotating community-led topic.", + endsAt: new Date("2026-10-21T02:00:00.000Z"), + eventUrl: "https://example.com/typescript-weekly", + id: "e43f00ec-4003-4855-afc6-6b9b7f654229", + locationAddress: null, + locationName: null, + mode: "online", + recurrenceCount: 8, + recurrenceEndDate: null, + recurrenceEndType: "after_occurrences", + recurrenceFrequency: "week", + recurrenceInterval: 1, + recurrenceMonthlyPattern: null, + recurrenceWeekdays: [2], + startsAt: new Date("2026-10-21T01:00:00.000Z"), + submitterEmail: "jordan.lee@example.com", + submitterName: "Jordan Lee", + timezone: "America/Los_Angeles", + title: "Sacramento TypeScript Weekly", + }, +] satisfies PendingEvents; + +export const PENDING_ADMIN_EVENT_EDITS = [ + { + createdAt: new Date("2026-09-05T19:20:00.000Z"), + currentDescription: + "A weekly coworking session for people building civic technology.", + currentEndsAt: new Date("2026-10-22T03:00:00.000Z"), + currentEventUrl: "https://example.com/civic-tech-coworking", + currentLocationAddress: "915 I Street, Sacramento, CA 95814", + currentLocationName: "Atrium 916", + currentMode: "hybrid", + currentRecurrenceCount: 12, + currentRecurrenceEndDate: null, + currentRecurrenceEndType: "after_occurrences", + currentRecurrenceFrequency: "week", + currentRecurrenceInterval: 1, + currentRecurrenceMonthlyPattern: null, + currentRecurrenceWeekdays: [3], + currentStartsAt: new Date("2026-10-22T01:00:00.000Z"), + currentTitle: "Civic Tech Coworking", + description: + "A weekly coworking and project clinic for anyone improving public services with technology.", + endsAt: new Date("2026-10-22T03:30:00.000Z"), + eventId: "cf679f00-5bb3-4d59-b782-1825b4ac78e4", + eventUrl: "https://example.com/civic-tech-project-clinic", + hasCurrentOccurrenceOverride: false, + id: "08d3130a-2097-42a1-9319-a53cd149c625", + locationAddress: "915 I Street, Sacramento, CA 95814", + locationName: "Atrium 916", + mode: "hybrid", + occurrenceDate: null, + proposerEmail: "priya.shah@example.com", + proposerName: "Priya Shah", + recurrenceCount: null, + recurrenceEndDate: "2027-01-27", + recurrenceEndType: "on_date", + recurrenceFrequency: "week", + recurrenceInterval: 1, + recurrenceMonthlyPattern: null, + recurrenceWeekdays: [3], + scope: "series", + seriesEndsAt: new Date("2026-10-22T03:00:00.000Z"), + seriesStartsAt: new Date("2026-10-22T01:00:00.000Z"), + startsAt: new Date("2026-10-22T01:30:00.000Z"), + timezone: "America/Los_Angeles", + title: "Civic Tech Project Clinic", + }, +] satisfies PendingEventEdits; + +export const STORY_MANAGED_USERS = { + administrator: { + banned: false, + email: "samira.patel@example.com", + id: "storybook-administrator", + name: "Samira Patel", + roles: ["admin", "approver", "submitter"], + }, + approver: { + banned: false, + email: "devon.brooks@example.com", + id: "storybook-approver", + name: "Devon Brooks", + roles: ["approver", "submitter"], + }, + bannedSubmitter: { + banned: true, + email: "taylor.morgan@example.com", + id: "storybook-banned-submitter", + name: "Taylor Morgan", + roles: ["submitter"], + }, + submitter: { + banned: false, + email: "alex.rivera@example.com", + id: "storybook-submitter", + name: "Alex Rivera", + roles: ["submitter"], + }, +} as const satisfies Record; + +const listedUserTimestamps = { + createdAt: new Date("2026-01-15T18:00:00.000Z"), + updatedAt: new Date("2026-08-20T18:00:00.000Z"), +}; + +export const CURRENT_ADMIN_LISTED_USER = { + ...listedUserTimestamps, + banExpires: null, + banReason: null, + banned: false, + email: "current.admin@example.com", + emailVerified: true, + id: "storybook-current-admin", + image: null, + name: "Current Admin", + role: "admin", +}; + +export const OTHER_LISTED_USERS = Object.values(STORY_MANAGED_USERS).map( + (user) => ({ + ...listedUserTimestamps, + banExpires: null, + banReason: user.banned ? "Banned by a SacTech administrator." : null, + banned: user.banned, + email: user.email, + emailVerified: true, + id: user.id, + image: null, + name: user.name, + role: user.roles.join(","), + }), +); diff --git a/stories/fixtures/auth.ts b/stories/fixtures/auth.ts new file mode 100644 index 0000000..aa5bd88 --- /dev/null +++ b/stories/fixtures/auth.ts @@ -0,0 +1,46 @@ +import type { AuthSession } from "@/lib/auth"; + +interface SessionOptions { + banned?: boolean; + email?: string; + id?: string; + name?: string; + role?: string; +} + +export function createSession({ + banned = false, + email = "alex@example.com", + id = "storybook-user", + name = "Alex Rivera", + role = "submitter", +}: SessionOptions = {}): AuthSession { + const createdAt = new Date("2026-01-15T18:00:00.000Z"); + const updatedAt = new Date("2026-08-20T18:00:00.000Z"); + + return { + session: { + createdAt, + expiresAt: new Date("2027-01-15T18:00:00.000Z"), + id: "storybook-session", + ipAddress: "127.0.0.1", + token: "storybook-session-token", + updatedAt, + userAgent: "Storybook", + userId: id, + }, + user: { + banExpires: null, + banReason: null, + banned, + createdAt, + email, + emailVerified: true, + id, + image: null, + name, + role, + updatedAt, + }, + } as AuthSession; +} diff --git a/stories/fixtures/events.ts b/stories/fixtures/events.ts new file mode 100644 index 0000000..23c5487 --- /dev/null +++ b/stories/fixtures/events.ts @@ -0,0 +1,148 @@ +import type { Event, EventBlock, RecurrenceRule } from "@/app/events/types"; +import { SACRAMENTO_TIME_ZONE } from "@/lib/events/constants"; + +export const EVENT_STORY_REFERENCE_DATE = "2026-09-01"; + +export function createEventBlock( + overrides: Partial = {}, +): EventBlock { + return { + description: "A welcoming Sacramento technology gathering.", + ends_at: new Date("2026-09-05T21:00:00.000Z"), + in_person: true, + is_online: false, + location_address: "123 J Street, Sacramento, CA", + location_description: "The Urban Hive", + location_url: "https://events.example.com/details", + presenters: [], + slug: "event-occurrence", + starts_at: new Date("2026-09-05T19:00:00.000Z"), + timezone: SACRAMENTO_TIME_ZONE, + title: "Event occurrence", + ...overrides, + }; +} + +export function createWeeklyRecurrenceRule( + overrides: Partial = {}, +): RecurrenceRule { + return { + endDate: null, + endType: "after_occurrences", + excludedDates: ["2026-09-09"], + frequency: "week", + interval: 1, + monthlyPattern: null, + occurrenceCount: 8, + weekdays: [3], + ...overrides, + }; +} + +export function createRecurringEvent(overrides: Partial = {}): Event { + const description = + overrides.description ?? + "Practice TypeScript with welcoming Sacramento developers."; + const title = overrides.title ?? "Sacramento TypeScript Weekly"; + const blocks = overrides.blocks ?? [ + createEventBlock({ + description, + ends_at: new Date("2026-09-02T18:00:00.000Z"), + in_person: false, + is_online: true, + location_address: undefined, + location_description: "Online", + slug: "typescript-weekly-seed", + starts_at: new Date("2026-09-02T17:00:00.000Z"), + title, + }), + ]; + + return { + description, + has_event_page: true, + in_person: false, + is_online: true, + is_recurring: true, + location_address: undefined, + location_description: "Online", + location_url: "https://events.example.com/typescript-weekly", + organizers: [], + recurrence_rule: createWeeklyRecurrenceRule(), + slug: "sacramento-typescript-weekly", + title, + ...overrides, + blocks, + }; +} + +export function createRecurringEventWithOverride( + overrides: Partial = {}, +): Event { + const event = createRecurringEvent(); + + return createRecurringEvent({ + ...event, + blocks: [ + ...event.blocks, + createEventBlock({ + description: "Bring a laptop for a special hands-on TypeScript night.", + ends_at: new Date("2026-09-16T03:00:00.000Z"), + in_person: true, + is_online: false, + location_address: "123 J Street, Sacramento, CA", + location_description: "The Urban Hive", + location_url: "https://events.example.com/hands-on-night", + recurrence_date: "2026-09-16", + slug: "typescript-weekly-2026-09-16-override", + starts_at: new Date("2026-09-16T01:30:00.000Z"), + title: "TypeScript Hands-on Night", + }), + ], + recurrence_rule: createWeeklyRecurrenceRule({ + excludedDates: ["2026-09-09", "2026-09-16"], + }), + ...overrides, + }); +} + +export function createSpecialEvent(overrides: Partial = {}): Event { + const description = + overrides.description ?? "A one-day design community event."; + const title = overrides.title ?? "Sacramento Design Summit"; + const blocks = overrides.blocks ?? [ + createEventBlock({ + description, + location_description: "The Urban Hive", + location_url: "https://events.example.com/design-summit", + slug: "design-summit-occurrence", + title, + }), + ]; + + return { + description, + has_event_page: true, + in_person: true, + is_online: false, + is_recurring: false, + location_address: "123 J Street, Sacramento, CA", + location_description: "The Urban Hive", + location_url: "https://events.example.com/design-summit", + organizers: [], + recurrence_rule: null, + slug: "sacramento-design-summit", + title, + ...overrides, + blocks, + }; +} + +export function createLongEventDescription(finalSentence: string) { + return `${"Sacramento community members share practical ideas and make room for generous conversation. ".repeat(5)}${finalSentence}`; +} + +export const MIXED_EVENTS: Event[] = [ + createRecurringEventWithOverride(), + createSpecialEvent(), +]; diff --git a/stories/fixtures/managed-event.ts b/stories/fixtures/managed-event.ts new file mode 100644 index 0000000..91ba4f0 --- /dev/null +++ b/stories/fixtures/managed-event.ts @@ -0,0 +1,45 @@ +import type { getManagedEventForEdit } from "@/lib/events/queries"; + +export type ManagedEventFixture = NonNullable< + Awaited> +>; + +export function createManagedEvent( + overrides: Partial = {}, +): ManagedEventFixture { + return { + canceledAt: null, + canceledOccurrences: ["2026-11-05"], + changeRequests: [], + collaborators: [ + { + email: "jamie@example.com", + name: "Jamie Chen", + userId: "collaborator-jamie", + }, + ], + description: + "A weekly meetup for Sacramento developers to learn together and share work in progress.", + endsAt: new Date("2026-10-15T03:30:00.000Z"), + eventUrl: "https://example.com/sacramento-typescript", + id: "storybook-managed-event", + isOwner: true, + locationAddress: "828 I Street, Sacramento, CA 95814", + locationName: "Sacramento Central Library", + mode: "hybrid", + occurrenceOverride: null, + recurrenceCount: null, + recurrenceEndDate: "2027-02-25", + recurrenceEndType: "on_date", + recurrenceFrequency: "week", + recurrenceInterval: 1, + recurrenceMonthlyPattern: null, + recurrenceWeekdays: [4], + startsAt: new Date("2026-10-15T01:30:00.000Z"), + status: "approved", + submittedBy: "storybook-user", + timezone: "America/Los_Angeles", + title: "Sacramento TypeScript Weekly", + ...overrides, + }; +} diff --git a/stories/fixtures/submissions.ts b/stories/fixtures/submissions.ts new file mode 100644 index 0000000..2d2fb0b --- /dev/null +++ b/stories/fixtures/submissions.ts @@ -0,0 +1,100 @@ +import type { getSubmissionsForUser } from "@/lib/events/queries"; + +export type SubmissionFixture = Awaited< + ReturnType +>[number]; + +export function createSubmission( + overrides: Partial = {}, +): SubmissionFixture { + return { + canceledAt: null, + canceledOccurrences: [], + changeRequests: [], + collaborators: [], + createdAt: new Date("2026-08-20T18:00:00.000Z"), + endsAt: new Date("2026-10-15T03:30:00.000Z"), + id: "storybook-submission", + isOwner: true, + moderationNote: null, + occurrenceOverrides: [], + recurrenceCount: null, + recurrenceEndDate: "2027-02-25", + recurrenceEndType: "on_date", + recurrenceFrequency: "week", + recurrenceInterval: 1, + recurrenceMonthlyPattern: null, + recurrenceWeekdays: [4], + startsAt: new Date("2026-10-15T01:30:00.000Z"), + status: "approved", + submittedBy: "storybook-user", + title: "Sacramento TypeScript Weekly", + ...overrides, + }; +} + +export const mixedSubmissions: SubmissionFixture[] = [ + createSubmission({ + canceledOccurrences: ["2026-11-05", "2026-12-24"], + changeRequests: [ + { + createdAt: new Date("2026-08-30T18:00:00.000Z"), + eventId: "storybook-submission", + id: "pending-change", + moderationNote: null, + occurrenceDate: null, + scope: "series", + status: "pending", + }, + ], + }), + createSubmission({ + changeRequests: [ + { + createdAt: new Date("2026-08-29T18:00:00.000Z"), + eventId: "shared-demo-night", + id: "rejected-change", + moderationNote: "Please add the venue's accessibility details.", + occurrenceDate: null, + scope: "series", + status: "rejected", + }, + ], + endsAt: new Date("2026-10-22T03:00:00.000Z"), + id: "shared-demo-night", + isOwner: false, + recurrenceEndDate: null, + recurrenceEndType: null, + recurrenceFrequency: null, + recurrenceInterval: null, + recurrenceWeekdays: null, + startsAt: new Date("2026-10-22T01:00:00.000Z"), + submittedBy: "another-user", + title: "Sacramento Community Demo Night", + }), + createSubmission({ + endsAt: new Date("2026-10-29T03:00:00.000Z"), + id: "pending-design-meetup", + moderationNote: null, + recurrenceEndDate: null, + recurrenceEndType: null, + recurrenceFrequency: null, + recurrenceInterval: null, + recurrenceWeekdays: null, + startsAt: new Date("2026-10-29T01:00:00.000Z"), + status: "pending", + title: "Design Systems Meetup", + }), + createSubmission({ + canceledAt: new Date("2026-08-28T18:00:00.000Z"), + endsAt: new Date("2026-09-18T03:00:00.000Z"), + id: "canceled-workshop", + recurrenceEndDate: null, + recurrenceEndType: null, + recurrenceFrequency: null, + recurrenceInterval: null, + recurrenceWeekdays: null, + startsAt: new Date("2026-09-18T01:00:00.000Z"), + title: "Canceled Accessibility Workshop", + }), +]; diff --git a/stories/story-coverage.test.ts b/stories/story-coverage.test.ts new file mode 100644 index 0000000..5cbf3cd --- /dev/null +++ b/stories/story-coverage.test.ts @@ -0,0 +1,42 @@ +import { existsSync, globSync } from "node:fs"; +import { resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +const projectRoot = fileURLToPath(new URL("../", import.meta.url)); +const sourcePatterns = ["app/**/*.tsx", "components/**/*.tsx"]; +const excludedSourcePatterns = [ + "**/*.test.tsx", + "**/*.integration.test.tsx", + "**/*.stories.tsx", + "app/layout.tsx", + "app/**/opengraph-image.tsx", + "app/**/twitter-image.tsx", + "app/**/icon.tsx", + "app/**/apple-icon.tsx", +]; + +function expectedStoryPath(sourcePath: string) { + return `stories/${sourcePath.replace(/\.tsx$/, ".stories.tsx")}`; +} + +describe("Storybook story coverage", () => { + it("has a mirrored story for every page and component", () => { + const sourcePaths = sourcePatterns + .flatMap((pattern) => + globSync(pattern, { + cwd: projectRoot, + exclude: excludedSourcePatterns, + }), + ) + .sort(); + const missingStoryPaths = sourcePaths + .map(expectedStoryPath) + .filter((storyPath) => !existsSync(resolve(projectRoot, storyPath))); + + expect( + missingStoryPaths, + `Missing mirrored Storybook stories:\n${missingStoryPaths.join("\n")}`, + ).toEqual([]); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 63b95cf..8bafa52 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "target": "ES2017", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, + "allowImportingTsExtensions": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, From aed5efc659e981eb189d014dea7b18ba2593356c Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Tue, 1 Sep 2026 16:50:03 -0700 Subject: [PATCH 4/6] chore: load all pages now --- app/admin/users/__mocks__/actions.ts | 6 +++ lib/__mocks__/auth-client.ts | 15 +++++++ lib/__mocks__/auth-config.ts | 8 ++++ lib/__mocks__/email-delivery.ts | 4 ++ lib/__mocks__/session.ts | 42 +++++++++++++++++++ lib/events/__mocks__/actions.ts | 10 +++++ lib/events/__mocks__/queries.ts | 7 ++++ .../admin/events/moderation-form.stories.tsx | 15 +++---- 8 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 app/admin/users/__mocks__/actions.ts create mode 100644 lib/__mocks__/auth-client.ts create mode 100644 lib/__mocks__/auth-config.ts create mode 100644 lib/__mocks__/email-delivery.ts create mode 100644 lib/__mocks__/session.ts create mode 100644 lib/events/__mocks__/actions.ts create mode 100644 lib/events/__mocks__/queries.ts diff --git a/app/admin/users/__mocks__/actions.ts b/app/admin/users/__mocks__/actions.ts new file mode 100644 index 0000000..5a45e8f --- /dev/null +++ b/app/admin/users/__mocks__/actions.ts @@ -0,0 +1,6 @@ +import { fn } from "storybook/test"; + +const idleResult = () => ({ message: "", status: "idle" as const }); + +export const updateUserBan = fn(async () => idleResult()); +export const updateUserRoles = fn(async () => idleResult()); diff --git a/lib/__mocks__/auth-client.ts b/lib/__mocks__/auth-client.ts new file mode 100644 index 0000000..0f042e3 --- /dev/null +++ b/lib/__mocks__/auth-client.ts @@ -0,0 +1,15 @@ +import { fn } from "storybook/test"; + +const successfulResult = () => ({ data: null, error: null }); + +export const authClient = { + requestPasswordReset: fn(async () => successfulResult()), + resetPassword: fn(async () => successfulResult()), + signIn: { + email: fn(async () => successfulResult()), + }, + signOut: fn(async () => successfulResult()), + signUp: { + email: fn(async () => successfulResult()), + }, +}; diff --git a/lib/__mocks__/auth-config.ts b/lib/__mocks__/auth-config.ts new file mode 100644 index 0000000..4e06576 --- /dev/null +++ b/lib/__mocks__/auth-config.ts @@ -0,0 +1,8 @@ +import { fn } from "storybook/test"; + +export const auth = { + api: { + getSession: fn(async () => null), + listUsers: fn(async () => ({ total: 0, users: [] })), + }, +}; diff --git a/lib/__mocks__/email-delivery.ts b/lib/__mocks__/email-delivery.ts new file mode 100644 index 0000000..9f86124 --- /dev/null +++ b/lib/__mocks__/email-delivery.ts @@ -0,0 +1,4 @@ +import { fn } from "storybook/test"; + +export const isEmailDeliveryEnabled = fn(() => true); +export const isLocalDevelopmentEnvironment = fn(() => false); diff --git a/lib/__mocks__/session.ts b/lib/__mocks__/session.ts new file mode 100644 index 0000000..fd7d1fe --- /dev/null +++ b/lib/__mocks__/session.ts @@ -0,0 +1,42 @@ +import { fn } from "storybook/test"; + +interface SessionLike { + user: { + role?: string | null; + }; +} + +function rolesFor(session: SessionLike | null) { + return new Set( + session?.user.role + ?.split(",") + .map((role) => role.trim()) + .filter(Boolean) ?? ["submitter"], + ); +} + +export const getCurrentSession = fn(async () => null); +export const requireSession = fn(async () => null); +export const requireAdminSession = fn(async () => null); +export const requireEventReviewerSession = fn(async () => null); + +export const sessionIsAdmin = fn((session: SessionLike | null) => + rolesFor(session).has("admin"), +); + +export const sessionCanSubmitEvents = fn( + (session: SessionLike | null) => session !== null, +); + +export const sessionCanCancelOwnEvents = fn( + (session: SessionLike | null) => session !== null, +); + +export const sessionCanReviewEvents = fn((session: SessionLike | null) => { + const roles = rolesFor(session); + return roles.has("admin") || roles.has("approver"); +}); + +export const sessionCanManageUsers = fn((session: SessionLike | null) => + rolesFor(session).has("admin"), +); diff --git a/lib/events/__mocks__/actions.ts b/lib/events/__mocks__/actions.ts new file mode 100644 index 0000000..b43934e --- /dev/null +++ b/lib/events/__mocks__/actions.ts @@ -0,0 +1,10 @@ +import { fn } from "storybook/test"; + +const idleResult = () => ({ message: "", status: "idle" as const }); + +export const cancelEvent = fn(async () => idleResult()); +export const inviteEventCollaborator = fn(async () => idleResult()); +export const moderateEvent = fn(async () => idleResult()); +export const moderateEventEdit = fn(async () => idleResult()); +export const requestEventEdit = fn(async () => idleResult()); +export const submitEvent = fn(async () => idleResult()); diff --git a/lib/events/__mocks__/queries.ts b/lib/events/__mocks__/queries.ts new file mode 100644 index 0000000..798008c --- /dev/null +++ b/lib/events/__mocks__/queries.ts @@ -0,0 +1,7 @@ +import { fn } from "storybook/test"; + +export const getApprovedEvents = fn(async () => []); +export const getManagedEventForEdit = fn(async () => null); +export const getPendingEventEdits = fn(async () => []); +export const getPendingEvents = fn(async () => []); +export const getSubmissionsForUser = fn(async () => []); diff --git a/stories/app/admin/events/moderation-form.stories.tsx b/stories/app/admin/events/moderation-form.stories.tsx index f039f9f..a8f678f 100644 --- a/stories/app/admin/events/moderation-form.stories.tsx +++ b/stories/app/admin/events/moderation-form.stories.tsx @@ -29,7 +29,11 @@ function mockIdleActions() { } export const IdleEvent = meta.story({ - args: {}, + args: { + eventId: "9ae8c027-41d6-4890-a431-1d8208b22e40", + eventTitle: "Sacramento Community Demo Night", + reviewType: "event", + }, beforeEach: mockIdleActions, play: async ({ canvas }) => { await expect( @@ -60,8 +64,7 @@ export const IdleChangeRequest = meta.story({ }, }); -export const PendingDecision = meta.story({ - args: {}, +export const PendingDecision = IdleEvent.extend({ beforeEach: () => { mockIdleActions(); mocked(moderateEvent).mockImplementation(() => new Promise(() => {})); @@ -80,8 +83,7 @@ export const PendingDecision = meta.story({ }, }); -export const RejectionError = meta.story({ - args: {}, +export const RejectionError = IdleEvent.extend({ beforeEach: () => { mockIdleActions(); mocked(moderateEvent).mockResolvedValue({ @@ -105,8 +107,7 @@ export const RejectionError = meta.story({ }, }); -export const Approved = meta.story({ - args: {}, +export const Approved = IdleEvent.extend({ beforeEach: () => { mockIdleActions(); mocked(moderateEvent).mockResolvedValue({ From d26e9cb14bc4db96d2fb1c0476b713fbd2416baf Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Tue, 1 Sep 2026 16:54:42 -0700 Subject: [PATCH 5/6] chore: various fixes --- stories/app/admin/events/page.stories.tsx | 8 ++++++-- stories/app/events/[eventId]/edit/page.stories.tsx | 4 +++- stories/fixtures/managed-event.ts | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/stories/app/admin/events/page.stories.tsx b/stories/app/admin/events/page.stories.tsx index 3ca1228..a1d1715 100644 --- a/stories/app/admin/events/page.stories.tsx +++ b/stories/app/admin/events/page.stories.tsx @@ -81,7 +81,11 @@ export const PopulatedQueues = meta.story({ await expect( canvas.getByRole("heading", { name: "Civic Tech Project Clinic" }), ).toBeVisible(); - await expect(canvas.getByText("2 events waiting")).toBeVisible(); - await expect(canvas.getByText("1 change waiting")).toBeVisible(); + await expect(canvas.getByText("events waiting")).toHaveTextContent( + "2 events waiting", + ); + await expect(canvas.getByText("change waiting")).toHaveTextContent( + "1 change waiting", + ); }, }); diff --git a/stories/app/events/[eventId]/edit/page.stories.tsx b/stories/app/events/[eventId]/edit/page.stories.tsx index 5759c07..feb0c6b 100644 --- a/stories/app/events/[eventId]/edit/page.stories.tsx +++ b/stories/app/events/[eventId]/edit/page.stories.tsx @@ -7,7 +7,9 @@ import { createManagedEvent } from "@/stories/fixtures/managed-event"; import { mocked } from "storybook/test"; const seriesArgs = { - params: Promise.resolve({ eventId: "storybook-managed-event" }), + params: Promise.resolve({ + eventId: "00000000-0000-4000-8000-000000000001", + }), searchParams: Promise.resolve({ scope: "series" }), }; diff --git a/stories/fixtures/managed-event.ts b/stories/fixtures/managed-event.ts index 91ba4f0..6d08ec2 100644 --- a/stories/fixtures/managed-event.ts +++ b/stories/fixtures/managed-event.ts @@ -22,7 +22,7 @@ export function createManagedEvent( "A weekly meetup for Sacramento developers to learn together and share work in progress.", endsAt: new Date("2026-10-15T03:30:00.000Z"), eventUrl: "https://example.com/sacramento-typescript", - id: "storybook-managed-event", + id: "00000000-0000-4000-8000-000000000001", isOwner: true, locationAddress: "828 I Street, Sacramento, CA 95814", locationName: "Sacramento Central Library", From ccdd94b78f6c5cd5615dd7991ab5626fe66cef51 Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Tue, 1 Sep 2026 16:58:28 -0700 Subject: [PATCH 6/6] chore: upgrade Vitest --- package.json | 6 +- pnpm-lock.yaml | 173 +++++++++++++++++++++++++------------------------ 2 files changed, 92 insertions(+), 87 deletions(-) diff --git a/package.json b/package.json index 681846c..ba0b058 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,8 @@ "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^6.0.5", - "@vitest/browser": "5.0.0-beta.7", - "@vitest/browser-playwright": "5.0.0-beta.7", + "@vitest/browser": "5.0.0-rc.4", + "@vitest/browser-playwright": "5.0.0-rc.4", "auth": "^1.7.1", "drizzle-kit": "1.0.0-rc.4", "eslint": "^9.31.0", @@ -75,7 +75,7 @@ "prettier": "3.9.6", "typescript": "^6.0.3", "vite": "^8.2.1", - "vitest": "5.0.0-beta.7", + "vitest": "5.0.0-rc.4", "storybook": "^10.5.10", "@storybook/nextjs-vite": "^10.5.10", "@storybook/addon-mcp": "^0.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b286a2..860a40f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,7 +46,7 @@ importers: version: 2.1.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) better-auth: specifier: 1.7.1 - version: 1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-beta.7) + version: 1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-rc.4) dayjs: specifier: ^1.11.23 version: 1.11.23 @@ -92,7 +92,7 @@ importers: version: 6.9.2(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@storybook/addon-mcp': specifier: ^0.7.0 - version: 0.7.0(@storybook/addon-vitest@10.5.10(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/browser@5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-beta.7))(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(typescript@6.0.3) + version: 0.7.0(@storybook/addon-vitest@10.5.10(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/browser@5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-rc.4))(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(typescript@6.0.3) '@storybook/nextjs-vite': specifier: ^10.5.10 version: 10.5.10(@babel/core@7.29.7(supports-color@10.2.2))(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.1)(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(supports-color@10.2.2)(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) @@ -101,7 +101,7 @@ importers: version: 10.4.1 '@testing-library/jest-dom': specifier: ^7.0.1 - version: 7.0.1(@testing-library/dom@10.4.1)(vitest@5.0.0-beta.7) + version: 7.0.1(@testing-library/dom@10.4.1)(vitest@5.0.0-rc.4) '@testing-library/react': specifier: ^16.3.2 version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -118,14 +118,14 @@ importers: specifier: ^6.0.5 version: 6.0.5(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) '@vitest/browser': - specifier: 5.0.0-beta.7 - version: 5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7) + specifier: 5.0.0-rc.4 + version: 5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4) '@vitest/browser-playwright': - specifier: 5.0.0-beta.7 - version: 5.0.0-beta.7(playwright@1.62.1)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7) + specifier: 5.0.0-rc.4 + version: 5.0.0-rc.4(playwright@1.62.1)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4) auth: specifier: ^1.7.1 - version: 1.7.1(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(chokidar@5.0.0)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1)(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2)(vitest@5.0.0-beta.7) + version: 1.7.1(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(chokidar@5.0.0)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1)(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2)(vitest@5.0.0-rc.4) drizzle-kit: specifier: 1.0.0-rc.4 version: 1.0.0-rc.4 @@ -160,8 +160,8 @@ importers: specifier: ^8.2.1 version: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) vitest: - specifier: 5.0.0-beta.7 - version: 5.0.0-beta.7(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/ui@5.0.0-beta.7)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + specifier: 5.0.0-rc.4 + version: 5.0.0-rc.4(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/ui@5.0.0-rc.4)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) packages: @@ -430,8 +430,8 @@ packages: '@better-fetch/fetch@1.3.1': resolution: {integrity: sha512-ABkD1WhyfPZprKRQI3bhATjeiFuNWC9PXhfGWqL+sg/gKrM977oFrYkdb4msM3hgUGonr7KlOsOFT5TU2rht9g==} - '@blazediff/core@1.9.1': - resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} + '@blazediff/core@1.10.0': + resolution: {integrity: sha512-AOQff0zgR7cGsZL+4E7hVkmujoPUpm0J9xzWGWZj5wCjd3gmxESXAPfKyuzs93VdpQNFhHlBhfOjrcZ+XTERtQ==} '@bramus/specificity@2.4.2': resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} @@ -3034,22 +3034,22 @@ packages: babel-plugin-react-compiler: optional: true - '@vitest/browser-playwright@5.0.0-beta.7': - resolution: {integrity: sha512-5YcoQ2FNxk3X37UILQxMjiq7PGziK8IqXSgps9+uj6dLbpCCn6vLKlrkjHJfL4v8W0+uwHrO8RnNjegHT0b72w==} + '@vitest/browser-playwright@5.0.0-rc.4': + resolution: {integrity: sha512-rU3GJlZiShdl3NOtjhi8mDw+bhZ0/vabFmJBnDDB0VxMRx0IQ8FJsRIJIFol22018C9zbU7/OK/W0EEThKxsyQ==} peerDependencies: playwright: '*' - vitest: 5.0.0-beta.7 + vitest: 5.0.0-rc.4 - '@vitest/browser@5.0.0-beta.7': - resolution: {integrity: sha512-zcO1dsyO5lyX+MhXkyOV+9UjCaUsFnq3LBy7XYLlfWnYQKrlxfNmbgRmgL9KhCRe5fpR3AM05jDlDEbbtaOGdQ==} + '@vitest/browser@5.0.0-rc.4': + resolution: {integrity: sha512-zXTk9lxJcbZcUN8eSrsD9JObNMxds+FEnANBk9hy8Fi3h3J051ubySjZtvjR0cghASYzN/m62k/WTwID1VLjqA==} peerDependencies: - vitest: 5.0.0-beta.7 + vitest: 5.0.0-rc.4 '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@vitest/mocker@5.0.0-beta.7': - resolution: {integrity: sha512-SczTGMNh6ksM5r8ogUwAf8zVbQPgbc6yvdZO9xBsTKT2rLUwpHn95Upod0s0Cpm3/bH898iqRBLXovRZSUbJBg==} + '@vitest/mocker@5.0.0-rc.4': + resolution: {integrity: sha512-8DLV37U8yxj9p7bXuRq5e60FvU9znAv4wrb2dP0+ElP53mZjAu5ZtwRCbqEVAoLcs7zSTHHXuNUQAuS+RWblCQ==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3062,25 +3062,25 @@ packages: '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/pretty-format@5.0.0-beta.7': - resolution: {integrity: sha512-mYUiYNj9BcF4BawuqncdUlR+zNNbWOWBNsp57jJon2Gq5R2fMqTYQJWHpfvkNqkAAgt/lC6bH5+JISjLIhHa4w==} + '@vitest/pretty-format@5.0.0-rc.4': + resolution: {integrity: sha512-knCZ2O6bxmCc8ihMqKh82cKmP4Gunbotl3I9wwgSgBFsBV4aB8JkOvslHv4NEBVnTsBvkxikQKLPkm24XU7+sg==} '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/spy@5.0.0-beta.7': - resolution: {integrity: sha512-NiE4frPDnDjFaMvaZaj4ASyxc/c2fvSQU8Fpo7D0YNj9AetE4hcA7ewRxhlrg6dHEOt1JhhCgy67e8SCXDh5VQ==} + '@vitest/spy@5.0.0-rc.4': + resolution: {integrity: sha512-Fti74i6fwpyXyAKOCfxA1KwGtIIEQlsHkaQj/jgO4GkeQDaXoACuqp2UdUn3B40L8C5L6nqk/rAI8n9js4u2VQ==} - '@vitest/ui@5.0.0-beta.7': - resolution: {integrity: sha512-z2PKZrRvT2W6S7s09cS9xqQf6CzURL0YbGCiaOvksIDQ8w/IS07g/0YujWY5c4CgavEaUmxctaDr/ru++U7aoA==} + '@vitest/ui@5.0.0-rc.4': + resolution: {integrity: sha512-KM1yR6pTo4ynRdR8fPKzTRTySClP9PQsOxdJoenB0gvAohchGWDwfvvhvrw5px7H7yp0RoycG24XLks05RmsTA==} peerDependencies: - vitest: 5.0.0-beta.7 + vitest: 5.0.0-rc.4 '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - '@vitest/utils@5.0.0-beta.7': - resolution: {integrity: sha512-DFjgVac0frIqGUZnQCd8lTGYfWnPJfzUNhOubZuvUMgzb+v0mdJjeMEcpmesujZIPasFOR+SzsQ4dS/BCEWGvg==} + '@vitest/utils@5.0.0-rc.4': + resolution: {integrity: sha512-69WxjuWTGTmtFFciMPPey/gilKM7sDR40Kj0rn5A+if3k0hzT8EjQs02/4MDp8lJMx2r0vovBIKzGsIfN1YJmw==} '@vue/compiler-core@3.5.41': resolution: {integrity: sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==} @@ -5778,8 +5778,8 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magic-string@1.2.1: - resolution: {integrity: sha512-vCfXkt3lIJha02CjPT1igeysyHVfCsEpIeD20O+X9aJ2hML3/kKx8E9Iv1FB+aMSAlDOEAtpRWzuooQCrYwdUg==} + magic-string@1.2.3: + resolution: {integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==} make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -6534,6 +6534,10 @@ packages: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + picoquery@2.5.0: resolution: {integrity: sha512-j1kgOFxtaCyoFCkpoYG2Oj3OdGakadO7HZ7o5CqyRazlmBekKhbDoUnNnXASE07xSY4nDImWZkrZv7toSxMi/g==} @@ -7402,8 +7406,8 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - tinybench@6.1.3: - resolution: {integrity: sha512-8k25iNSZHnzkjp3nrpEmx6YkrTNs41PzOHYc28DR5Z2l/CId/Nzw+Ume/41jCeDDmCUMPuK5GkQ/VxJaJ2P6Qg==} + tinybench@6.1.4: + resolution: {integrity: sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==} engines: {node: '>=20.0.0'} tinyclip@0.1.15: @@ -7861,20 +7865,20 @@ packages: yaml: optional: true - vitest@5.0.0-beta.7: - resolution: {integrity: sha512-YT/AWBSxJW/GHyNVQzVA7lIj39oRcEPEj1AYMi1Oko7l5GwCh8MO0ufavjVZI0oEYXcAoNmthegJhzGA2pkU2A==} + vitest@5.0.0-rc.4: + resolution: {integrity: sha512-Yd2zKNMlZ0k1iy4ar3DorcOJ+fLJmbMoawONMQwhIVRWnbbAURVWhQDk1s1eTp0Am7ZdZhk4nwu2m0IBRl5Dtw==} engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 5.0.0-beta.7 - '@vitest/browser-preview': 5.0.0-beta.7 + '@vitest/browser-playwright': 5.0.0-rc.4 + '@vitest/browser-preview': 5.0.0-rc.4 '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 - '@vitest/coverage-istanbul': 5.0.0-beta.7 - '@vitest/coverage-v8': 5.0.0-beta.7 - '@vitest/ui': 5.0.0-beta.7 + '@vitest/coverage-istanbul': 5.0.0-rc.4 + '@vitest/coverage-v8': 5.0.0-rc.4 + '@vitest/ui': 5.0.0-rc.4 happy-dom: '*' jsdom: '*' vite: ^6.4.0 || ^7.0.0 || ^8.0.0 @@ -8515,7 +8519,7 @@ snapshots: '@better-fetch/fetch@1.3.1': {} - '@blazediff/core@1.9.1': {} + '@blazediff/core@1.10.0': {} '@bramus/specificity@2.4.2': dependencies: @@ -10542,7 +10546,7 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@storybook/addon-mcp@0.7.0(@storybook/addon-vitest@10.5.10(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/browser@5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-beta.7))(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(typescript@6.0.3)': + '@storybook/addon-mcp@0.7.0(@storybook/addon-vitest@10.5.10(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/browser@5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-rc.4))(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(typescript@6.0.3)': dependencies: '@storybook/mcp': 0.8.0(typescript@6.0.3) '@tmcp/adapter-valibot': 0.1.6(tmcp@1.20.0(typescript@6.0.3))(valibot@1.2.0(typescript@6.0.3)) @@ -10552,20 +10556,20 @@ snapshots: tmcp: 1.20.0(typescript@6.0.3) valibot: 1.2.0(typescript@6.0.3) optionalDependencies: - '@storybook/addon-vitest': 10.5.10(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/browser@5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-beta.7) + '@storybook/addon-vitest': 10.5.10(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/browser@5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-rc.4) transitivePeerDependencies: - '@tmcp/auth' - typescript - '@storybook/addon-vitest@10.5.10(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/browser@5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-beta.7)': + '@storybook/addon-vitest@10.5.10(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/browser@5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8))(vitest@5.0.0-rc.4)': dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.1.0(react@19.2.8) storybook: 10.5.10(@types/react@19.2.18)(prettier@3.9.6)(react@19.2.8) optionalDependencies: - '@vitest/browser': 5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7) - '@vitest/browser-playwright': 5.0.0-beta.7(playwright@1.62.1)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7) - vitest: 5.0.0-beta.7(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/ui@5.0.0-beta.7)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + '@vitest/browser': 5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4) + '@vitest/browser-playwright': 5.0.0-rc.4(playwright@1.62.1)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4) + vitest: 5.0.0-rc.4(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/ui@5.0.0-rc.4)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) transitivePeerDependencies: - react optional: true @@ -10711,7 +10715,7 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/jest-dom@7.0.1(@testing-library/dom@10.4.1)(vitest@5.0.0-beta.7)': + '@testing-library/jest-dom@7.0.1(@testing-library/dom@10.4.1)(vitest@5.0.0-rc.4)': dependencies: '@adobe/css-tools': 4.5.0 '@testing-library/dom': 10.4.1 @@ -10721,7 +10725,7 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 optionalDependencies: - vitest: 5.0.0-beta.7(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/ui@5.0.0-beta.7)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + vitest: 5.0.0-rc.4(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/ui@5.0.0-rc.4)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: @@ -11085,30 +11089,30 @@ snapshots: '@rolldown/pluginutils': 1.0.1 vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) - '@vitest/browser-playwright@5.0.0-beta.7(playwright@1.62.1)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7)': + '@vitest/browser-playwright@5.0.0-rc.4(playwright@1.62.1)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4)': dependencies: - '@vitest/browser': 5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7) - '@vitest/mocker': 5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + '@vitest/browser': 5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4) + '@vitest/mocker': 5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) playwright: 1.62.1 tinyrainbow: 3.1.1 - vitest: 5.0.0-beta.7(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/ui@5.0.0-beta.7)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + vitest: 5.0.0-rc.4(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/ui@5.0.0-rc.4)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7)': + '@vitest/browser@5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4)': dependencies: - '@blazediff/core': 1.9.1 - '@vitest/mocker': 5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) - '@vitest/ui': 5.0.0-beta.7(vitest@5.0.0-beta.7) - '@vitest/utils': 5.0.0-beta.7 - magic-string: 1.2.1 + '@blazediff/core': 1.10.0 + '@vitest/mocker': 5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + '@vitest/ui': 5.0.0-rc.4(vitest@5.0.0-rc.4) + '@vitest/utils': 5.0.0-rc.4 + magic-string: 1.2.3 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.1 - vitest: 5.0.0-beta.7(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/ui@5.0.0-beta.7)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + vitest: 5.0.0-rc.4(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/ui@5.0.0-rc.4)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) ws: 8.21.3 transitivePeerDependencies: - bufferutil @@ -11124,12 +11128,12 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': + '@vitest/mocker@5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@jridgewell/trace-mapping': 0.3.31 - '@vitest/spy': 5.0.0-beta.7 + '@vitest/spy': 5.0.0-rc.4 estree-walker: 3.0.3 - magic-string: 1.2.1 + magic-string: 1.2.3 optionalDependencies: vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) @@ -11137,7 +11141,7 @@ snapshots: dependencies: tinyrainbow: 2.0.0 - '@vitest/pretty-format@5.0.0-beta.7': + '@vitest/pretty-format@5.0.0-rc.4': dependencies: tinyrainbow: 3.1.1 @@ -11145,18 +11149,17 @@ snapshots: dependencies: tinyspy: 4.0.4 - '@vitest/spy@5.0.0-beta.7': {} + '@vitest/spy@5.0.0-rc.4': {} - '@vitest/ui@5.0.0-beta.7(vitest@5.0.0-beta.7)': + '@vitest/ui@5.0.0-rc.4(vitest@5.0.0-rc.4)': dependencies: - '@vitest/utils': 5.0.0-beta.7 + '@vitest/utils': 5.0.0-rc.4 fflate: 0.8.3 flatted: 3.4.4 pathe: 2.0.3 sirv: 3.0.2 - tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vitest: 5.0.0-beta.7(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/ui@5.0.0-beta.7)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + vitest: 5.0.0-rc.4(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/ui@5.0.0-rc.4)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) '@vitest/utils@3.2.4': dependencies: @@ -11164,9 +11167,9 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 - '@vitest/utils@5.0.0-beta.7': + '@vitest/utils@5.0.0-rc.4': dependencies: - '@vitest/pretty-format': 5.0.0-beta.7 + '@vitest/pretty-format': 5.0.0-rc.4 convert-source-map: 2.0.0 tinyrainbow: 3.1.1 @@ -11448,7 +11451,7 @@ snapshots: stubborn-fs: 2.0.0 when-exit: 2.1.5 - auth@1.7.1(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(chokidar@5.0.0)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1)(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2)(vitest@5.0.0-beta.7): + auth@1.7.1(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(chokidar@5.0.0)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1)(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2)(vitest@5.0.0-rc.4): dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/preset-react': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) @@ -11458,7 +11461,7 @@ snapshots: '@better-auth/utils': 0.4.2 '@clack/prompts': 1.7.0 '@mrleebo/prisma-ast': 0.16.0 - better-auth: 1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-beta.7) + better-auth: 1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-rc.4) c12: 4.0.0-beta.5(chokidar@5.0.0)(dotenv@17.4.2)(jiti@2.7.0) chalk: 5.6.2 commander: 15.0.0 @@ -11574,7 +11577,7 @@ snapshots: jsonpointer: 5.0.1 leven: 3.1.0 - better-auth@1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-beta.7): + better-auth@1.7.1(@opentelemetry/api@1.9.1)(drizzle-kit@1.0.0-rc.4)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.0-rc.4): dependencies: '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1) '@better-auth/drizzle-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.9)(kysely@0.29.5)(nanostores@1.5.1))(@better-auth/utils@0.4.2)(drizzle-orm@1.0.0-rc.4(patch_hash=4106b3249c29efa09d6c2fcc4052e345c0aee75433c3047fd3aeec99fa0c0867)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(pg@8.23.0)(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3)) @@ -11600,7 +11603,7 @@ snapshots: pg: 8.23.0 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - vitest: 5.0.0-beta.7(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/ui@5.0.0-beta.7)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + vitest: 5.0.0-rc.4(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/ui@5.0.0-rc.4)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) transitivePeerDependencies: - '@cloudflare/workers-types' - '@opentelemetry/api' @@ -14018,7 +14021,7 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - magic-string@1.2.1: + magic-string@1.2.3: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -15130,6 +15133,8 @@ snapshots: picomatch@4.0.5: {} + picomatch@4.0.7: {} + picoquery@2.5.0: {} picospinner@3.1.2: {} @@ -16254,7 +16259,7 @@ snapshots: tiny-invariant@1.3.3: {} - tinybench@6.1.3: {} + tinybench@6.1.4: {} tinyclip@0.1.15: {} @@ -16672,18 +16677,18 @@ snapshots: jiti: 2.7.0 yaml: 2.9.0 - vitest@5.0.0-beta.7(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-beta.7)(@vitest/ui@5.0.0-beta.7)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)): + vitest@5.0.0-rc.4(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@5.0.0-rc.4)(@vitest/ui@5.0.0-rc.4)(jsdom@30.0.1(@noble/hashes@2.3.0))(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.0-beta.7(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + '@vitest/mocker': 5.0.0-rc.4(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) chai: 6.2.2 es-module-lexer: 2.3.2 expect-type: 1.4.0 - magic-string: 1.2.1 + magic-string: 1.2.3 obug: 2.1.4 - picomatch: 4.0.5 + picomatch: 4.0.7 std-env: 4.2.0 - tinybench: 6.1.3 + tinybench: 6.1.4 tinyexec: 1.3.0 tinyglobby: 0.2.17 vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) @@ -16691,8 +16696,8 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 24.13.3 - '@vitest/browser-playwright': 5.0.0-beta.7(playwright@1.62.1)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-beta.7) - '@vitest/ui': 5.0.0-beta.7(vitest@5.0.0-beta.7) + '@vitest/browser-playwright': 5.0.0-rc.4(playwright@1.62.1)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@5.0.0-rc.4) + '@vitest/ui': 5.0.0-rc.4(vitest@5.0.0-rc.4) jsdom: 30.0.1(@noble/hashes@2.3.0) transitivePeerDependencies: - msw