From 6c0cc677e16d78ef633cb151e59a3b6f0eaa4f34 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Fri, 3 Jul 2026 16:23:30 -0400 Subject: [PATCH 001/380] feat!: adopt @btst/yar defineRoute/defineRoutes and stabilize page component identity (v3.0.0) Migrate all client plugin routes to the declarative defineRoute/defineRoutes helpers from @btst/yar 1.3, replacing per-route createRoute handler closures and manual pageComponents fallback logic. BREAKING CHANGE: pageComponents overrides for parameterized routes now receive the route context ({ params }) as props instead of specific named props (slug, boardId, conversationId, etc). See docs/breaking-changes. Also memoize router.getRoute() in RouteRenderer so PageComponent keeps a stable identity across re-renders, preventing full subtree remounts, state loss, and Suspense re-triggering on every parent render. - Bump @btst/stack to 3.0.0; require @btst/yar >=1.3.0 - Re-export defineRoute/defineRoutes and RouteContext/RouteDef types from @btst/stack/plugins/client - Update codegen todo templates to defineRoute - Update plugin docs, shadcn-registry docs, and add v2 -> v3 migration guide Co-authored-by: Cursor --- docs/content/docs/breaking-changes.mdx | 60 + docs/content/docs/plugins/ai-chat.mdx | 15 +- docs/content/docs/plugins/blog.mdx | 17 +- docs/content/docs/plugins/cms.mdx | 23 +- docs/content/docs/plugins/development.mdx | 58 +- docs/content/docs/plugins/form-builder.mdx | 15 +- docs/content/docs/plugins/kanban.mdx | 11 +- docs/content/docs/plugins/ui-builder.mdx | 11 +- docs/content/docs/shadcn-registry.mdx | 26 +- packages/stack/package.json | 6 +- .../page-component-overrides.test.tsx | 120 +- .../stack/src/client/components/compose.tsx | 9 +- .../src/plugins/ai-chat/client/plugin.tsx | 88 +- .../stack/src/plugins/blog/client/plugin.tsx | 108 +- packages/stack/src/plugins/client/index.ts | 9 +- .../stack/src/plugins/cms/client/plugin.tsx | 102 +- .../src/plugins/comments/client/plugin.tsx | 14 +- .../plugins/form-builder/client/plugin.tsx | 80 +- .../src/plugins/kanban/client/plugin.tsx | 55 +- .../stack/src/plugins/media/client/plugin.tsx | 23 +- .../src/plugins/route-docs/client/plugin.tsx | 28 +- .../src/plugins/ui-builder/client/plugin.tsx | 54 +- pnpm-lock.yaml | 6604 ++--------------- .../nextjs/lib/plugins/todo/client/client.tsx | 38 +- .../app/lib/plugins/todo/client/client.tsx | 14 +- .../src/lib/plugins/todo/client/client.tsx | 14 +- 26 files changed, 1072 insertions(+), 6530 deletions(-) diff --git a/docs/content/docs/breaking-changes.mdx b/docs/content/docs/breaking-changes.mdx index 0a0b2fadc..fcbbe228e 100644 --- a/docs/content/docs/breaking-changes.mdx +++ b/docs/content/docs/breaking-changes.mdx @@ -10,6 +10,66 @@ This page documents breaking changes between major versions and provides migrati --- +## v2 → v3: Declarative routes and the `pageComponents` contract + +BTST v3 adopts the declarative `defineRoute` / `defineRoutes` helpers from `@btst/yar` 1.3+ for all plugin routes, and stabilizes page component identity across re-renders (no more subtree remounts or Suspense re-triggers on parent renders). + + + The only consumer-facing change is the props passed to `pageComponents` overrides on parameterized routes. Overrides for routes without params, and all other plugin config, are unchanged. + + +### `pageComponents` overrides now receive the route context + +Overrides for parameterized routes used to receive specific named props (e.g. `slug`, `boardId`, `conversationId`). They now receive the route context — `{ params }` — instead: + +```diff +blogClientPlugin({ + // ... other config + pageComponents: { + posts: MyCustomPostsPage, // unchanged (no params) +- post: ({ slug }) => , ++ post: ({ params }) => , +- tag: ({ tagSlug }) => , ++ tag: ({ params }) => , + }, +}) +``` + +Renamed props per plugin: + +| Plugin | Override | Old props | New props | +|--------|----------|-----------|-----------| +| Blog | `post`, `editPost` | `{ slug }` | `{ params: { slug } }` | +| Blog | `tag` | `{ tagSlug }` | `{ params: { tagSlug } }` | +| CMS | `contentList`, `newContent` | `{ typeSlug }` | `{ params: { typeSlug } }` | +| CMS | `editContent` | `{ typeSlug, id }` | `{ params: { typeSlug, id } }` | +| Form Builder | `editForm` | `{ id }` | `{ params: { id } }` | +| Form Builder | `submissions` | `{ formId }` | `{ params: { id } }` | +| UI Builder | `editPage` | `{ id }` | `{ params: { id } }` | +| Kanban | `board` | `{ boardId }` | `{ params: { boardId } }` | +| AI Chat | `chatConversation` | `{ conversationId }` | `{ params: { id } }` | + +### Custom plugins: `defineRoute` is the recommended route helper + +`createRoute` is still exported and fully supported, but plugin routes are simpler with `defineRoute`: + +```diff +- todos: createRoute("/todos", () => ({ +- PageComponent: TodosListPage, +- loader: todosLoader(config), +- meta: createTodosMeta(config, "/todos"), +- })), ++ todos: defineRoute("/todos", { ++ page: TodosListPage, ++ loader: todosLoader(config), ++ meta: createTodosMeta(config, "/todos"), ++ }), +``` + +`defineRoute`, `defineRoutes`, and the `RouteContext` / `RouteDef` types are re-exported from `@btst/stack/plugins/client`. + +--- + ## v1 → v2: Rebranding to BTST BTST v2 introduces a rebranding from "Better Stack" to "BTST". This guide covers all the changes you need to make to upgrade your project. diff --git a/docs/content/docs/plugins/ai-chat.mdx b/docs/content/docs/plugins/ai-chat.mdx index 9ef656617..1fef17ca4 100644 --- a/docs/content/docs/plugins/ai-chat.mdx +++ b/docs/content/docs/plugins/ai-chat.mdx @@ -378,7 +378,7 @@ The AI Chat plugin automatically creates the following pages (mounted at your co ### Page Component Overrides -You can replace any built-in page with your own React component using the optional `pageComponents` field in `aiChatClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided, so this is fully backward-compatible. +You can replace any built-in page with your own React component using the optional `pageComponents` field in `aiChatClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided. Overrides for parameterized routes receive the route context (`{ params }`) as props. ```tsx aiChatClientPlugin({ @@ -387,9 +387,9 @@ aiChatClientPlugin({ // Replace the chat home page chat: MyCustomChatPage, // Replace the conversation page (authenticated mode only) - // receives conversationId as a prop - chatConversation: ({ conversationId }) => ( - + // receives the route context as props + chatConversation: ({ params }) => ( + ), }, }) @@ -1326,8 +1326,11 @@ aiChatClientPlugin({ apiBasePath: "/api/data", queryClient, pageComponents: { - chat: ChatPageComponent, // replaces the chat home page - chatConversation: ChatConversationPageComponent, // replaces the conversation page + chat: ChatPageComponent, // replaces the chat home page + // Param routes receive the route context ({ params }) as props + chatConversation: ({ params }) => ( + + ), }, }) ``` diff --git a/docs/content/docs/plugins/blog.mdx b/docs/content/docs/plugins/blog.mdx index 99e0037a6..3655bc854 100644 --- a/docs/content/docs/plugins/blog.mdx +++ b/docs/content/docs/plugins/blog.mdx @@ -322,7 +322,7 @@ The blog plugin automatically creates the following pages (mounted at your confi ### Page Component Overrides -You can replace any built-in page with your own React component using the optional `pageComponents` field in `blogClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided, so this is fully backward-compatible. +You can replace any built-in page with your own React component using the optional `pageComponents` field in `blogClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided. Overrides for parameterized routes receive the route context (`{ params }`) as props. ```tsx blogClientPlugin({ @@ -330,12 +330,12 @@ blogClientPlugin({ pageComponents: { // Replace the published posts list page posts: MyCustomPostsPage, - // Replace the single post page — receives the slug as a prop - post: ({ slug }) => , - // Replace the edit post page — receives the slug as a prop - editPost: ({ slug }) => , - // Replace the tag page — receives tagSlug as a prop - tag: ({ tagSlug }) => , + // Replace the single post page — receives the route context as props + post: ({ params }) => , + // Replace the edit post page — receives the route context as props + editPost: ({ params }) => , + // Replace the tag page — receives the route context as props + tag: ({ params }) => , // Replace the drafts list page drafts: MyCustomDraftsPage, // Replace the new post page @@ -831,7 +831,8 @@ blogClientPlugin({ queryClient, pageComponents: { posts: HomePageComponent, // replaces the published posts list page - post: PostPageComponent, // replaces the single post page + // Param routes receive the route context ({ params }) as props + post: ({ params }) => , // drafts, newPost, editPost, tag — omit to keep built-in defaults }, }) diff --git a/docs/content/docs/plugins/cms.mdx b/docs/content/docs/plugins/cms.mdx index 6b6eae00a..c6e847d0e 100644 --- a/docs/content/docs/plugins/cms.mdx +++ b/docs/content/docs/plugins/cms.mdx @@ -335,7 +335,7 @@ Admin routes are automatically set to `noindex` for SEO. Don't include them in y ### Page Component Overrides -You can replace any built-in admin page with your own React component using the optional `pageComponents` field in `cmsClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided, so this is fully backward-compatible. +You can replace any built-in admin page with your own React component using the optional `pageComponents` field in `cmsClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided. Overrides for parameterized routes receive the route context (`{ params }`) as props. ```tsx cmsClientPlugin({ @@ -343,13 +343,15 @@ cmsClientPlugin({ pageComponents: { // Replace the CMS dashboard page dashboard: MyCustomDashboard, - // Replace the content list page — receives typeSlug as a prop - contentList: ({ typeSlug }) => , - // Replace the new content page — receives typeSlug as a prop - newContent: ({ typeSlug }) => , - // Replace the edit content page — receives typeSlug and id as props - editContent: ({ typeSlug, id }) => ( - + // Replace the content list page — receives the route context as props + contentList: ({ params }) => ( + + ), + // Replace the new content page — receives the route context as props + newContent: ({ params }) => , + // Replace the edit content page — receives the route context as props + editContent: ({ params }) => ( + ), }, }) @@ -1570,7 +1572,10 @@ cmsClientPlugin({ queryClient, pageComponents: { dashboard: DashboardPageComponent, // replaces the CMS dashboard page - contentList: ContentListPageComponent, // replaces the content list page + // Param routes receive the route context ({ params }) as props + contentList: ({ params }) => ( + + ), // newContent, editContent — omit to keep built-in defaults }, }) diff --git a/docs/content/docs/plugins/development.mdx b/docs/content/docs/plugins/development.mdx index 335e08108..f79187541 100644 --- a/docs/content/docs/plugins/development.mdx +++ b/docs/content/docs/plugins/development.mdx @@ -53,7 +53,8 @@ import { ```typescript import { defineClientPlugin, // Create a client plugin - createRoute, // Define a route + defineRoute, // Define a route declaratively + createRoute, // Low-level route with a handler closure createApiClient, // Type-safe API client isConnectionError // Detect build-time "no server" fetch failures } from "@btst/stack/plugins/client" @@ -498,7 +499,7 @@ The client plugin defines routes with React components, SSR data loaders, and SE ### Basic Structure ```typescript -import { createApiClient, defineClientPlugin, createRoute } from "@btst/stack/plugins/client" +import { createApiClient, defineClientPlugin, defineRoute } from "@btst/stack/plugins/client" import type { QueryClient } from "@tanstack/react-query" import type { TodosApiRouter } from "../api/backend" import { lazy } from "react" @@ -511,21 +512,19 @@ export interface TodosClientConfig { siteBasePath: string } +const TodosListPage = lazy(() => + import("./components").then((m) => ({ default: m.TodosListPage })) +) + export const todosClientPlugin = (config: TodosClientConfig) => defineClientPlugin({ name: "todos", routes: () => ({ - todos: createRoute("/todos", () => { - const TodosListPage = lazy(() => - import("./components").then((m) => ({ default: m.TodosListPage })) - ) - - return { - PageComponent: TodosListPage, - loader: todosLoader(config), - meta: createTodosMeta(config, "/todos"), - } + todos: defineRoute("/todos", { + page: TodosListPage, + loader: todosLoader(config), + meta: createTodosMeta(config, "/todos"), }), }), @@ -1187,7 +1186,7 @@ export type TodosApiRouter = ReturnType **File: `lib/plugins/todo/client/client.tsx`** ```typescript -import { createApiClient, defineClientPlugin, createRoute } from "@btst/stack/plugins/client" +import { createApiClient, defineClientPlugin, defineRoute } from "@btst/stack/plugins/client" import type { QueryClient } from "@tanstack/react-query" import type { TodosApiRouter } from "../api/backend" import { lazy } from "react" @@ -1223,6 +1222,13 @@ function todosLoader(config: TodosClientConfig) { } } +const TodosListPage = lazy(() => + import("./components").then((m) => ({ default: m.TodosListPage })) +) +const AddTodoPage = lazy(() => + import("./components").then((m) => ({ default: m.AddTodoPage })) +) + // Meta generator - create SEO tags from loaded data function createTodosMeta(config: TodosClientConfig, path: string) { return () => { @@ -1245,26 +1251,14 @@ export const todosClientPlugin = (config: TodosClientConfig) => name: "todos", routes: () => ({ - todos: createRoute("/todos", () => { - const TodosListPage = lazy(() => - import("./components").then((m) => ({ default: m.TodosListPage })) - ) - - return { - PageComponent: TodosListPage, - loader: todosLoader(config), - meta: createTodosMeta(config, "/todos"), - } + todos: defineRoute("/todos", { + page: TodosListPage, + loader: todosLoader(config), + meta: createTodosMeta(config, "/todos"), }), - addTodo: createRoute("/todos/add", () => { - const AddTodoPage = lazy(() => - import("./components").then((m) => ({ default: m.AddTodoPage })) - ) - - return { - PageComponent: AddTodoPage, - meta: createTodosMeta(config, "/todos/add"), - } + addTodo: defineRoute("/todos/add", { + page: AddTodoPage, + meta: createTodosMeta(config, "/todos/add"), }), }), diff --git a/docs/content/docs/plugins/form-builder.mdx b/docs/content/docs/plugins/form-builder.mdx index 7246ced78..383149faa 100644 --- a/docs/content/docs/plugins/form-builder.mdx +++ b/docs/content/docs/plugins/form-builder.mdx @@ -206,7 +206,7 @@ Admin routes are automatically set to `noindex` for SEO. Don't include them in y ### Page Component Overrides -You can replace any built-in admin page with your own React component using the optional `pageComponents` field in `formBuilderClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided, so this is fully backward-compatible. +You can replace any built-in admin page with your own React component using the optional `pageComponents` field in `formBuilderClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided. Overrides for parameterized routes receive the route context (`{ params }`) as props. ```tsx formBuilderClientPlugin({ @@ -216,10 +216,10 @@ formBuilderClientPlugin({ formList: MyCustomFormList, // Replace the new form page newForm: MyCustomNewForm, - // Replace the form editor page — receives id as a prop - editForm: ({ id }) => , - // Replace the form submissions page — receives formId as a prop - submissions: ({ formId }) => , + // Replace the form editor page — receives the route context as props + editForm: ({ params }) => , + // Replace the form submissions page — receives the route context as props + submissions: ({ params }) => , }, }) ``` @@ -890,8 +890,9 @@ formBuilderClientPlugin({ apiBasePath: "/api/data", queryClient, pageComponents: { - formList: FormListPageComponent, // replaces the form list page - editForm: EditFormPageComponent, // replaces the form editor page + formList: FormListPageComponent, // replaces the form list page + // Param routes receive the route context ({ params }) as props + editForm: ({ params }) => , // newForm, submissions — omit to keep built-in defaults }, }) diff --git a/docs/content/docs/plugins/kanban.mdx b/docs/content/docs/plugins/kanban.mdx index 305e7da37..f531db82f 100644 --- a/docs/content/docs/plugins/kanban.mdx +++ b/docs/content/docs/plugins/kanban.mdx @@ -313,7 +313,7 @@ The kanban plugin automatically creates the following pages (mounted at your con ### Page Component Overrides -You can replace any built-in page with your own React component using the optional `pageComponents` field in `kanbanClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided, so this is fully backward-compatible. +You can replace any built-in page with your own React component using the optional `pageComponents` field in `kanbanClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided. Overrides for parameterized routes receive the route context (`{ params }`) as props. ```tsx kanbanClientPlugin({ @@ -321,8 +321,8 @@ kanbanClientPlugin({ pageComponents: { // Replace the boards list page boards: MyCustomBoardsPage, - // Replace the board detail page — receives boardId as a prop - board: ({ boardId }) => , + // Replace the board detail page — receives the route context as props + board: ({ params }) => , // Replace the new board page newBoard: MyCustomNewBoardPage, }, @@ -1050,8 +1050,9 @@ kanbanClientPlugin({ apiBasePath: "/api/data", queryClient, pageComponents: { - boards: BoardsPageComponent, // replaces the boards list page - board: BoardPageComponent, // replaces the board detail page + boards: BoardsPageComponent, // replaces the boards list page + // Param routes receive the route context ({ params }) as props + board: ({ params }) => , // newBoard — omit to keep built-in default }, }) diff --git a/docs/content/docs/plugins/ui-builder.mdx b/docs/content/docs/plugins/ui-builder.mdx index f9ffc65ea..108fd42bb 100644 --- a/docs/content/docs/plugins/ui-builder.mdx +++ b/docs/content/docs/plugins/ui-builder.mdx @@ -213,7 +213,7 @@ Admin routes are automatically set to `noindex` for SEO. Don't include them in y ### Page Component Overrides -You can replace any built-in admin page with your own React component using the optional `pageComponents` field in `uiBuilderClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided, so this is fully backward-compatible. +You can replace any built-in admin page with your own React component using the optional `pageComponents` field in `uiBuilderClientPlugin(config)`. The built-in component is used as the fallback whenever an override is not provided. Overrides for parameterized routes receive the route context (`{ params }`) as props. ```tsx uiBuilderClientPlugin({ @@ -223,8 +223,8 @@ uiBuilderClientPlugin({ pageList: MyCustomPageList, // Replace the new page builder page newPage: MyCustomNewPage, - // Replace the edit page builder page — receives id as a prop - editPage: ({ id }) => , + // Replace the edit page builder page — receives the route context as props + editPage: ({ params }) => , }, }) ``` @@ -1140,8 +1140,9 @@ uiBuilderClientPlugin({ apiBasePath: "/api/data", queryClient, pageComponents: { - pageList: PageListPageComponent, // replaces the page list page - editPage: EditPagePageComponent, // replaces the page builder editor + pageList: PageListPageComponent, // replaces the page list page + // Param routes receive the route context ({ params }) as props + editPage: ({ params }) => , // newPage — omit to keep built-in default }, }) diff --git a/docs/content/docs/shadcn-registry.mdx b/docs/content/docs/shadcn-registry.mdx index 3b228e578..1158e93f6 100644 --- a/docs/content/docs/shadcn-registry.mdx +++ b/docs/content/docs/shadcn-registry.mdx @@ -126,6 +126,8 @@ Or install a single plugin's UI directly: After the install, most plugins let you import your ejected components and pass them to the client plugin via `pageComponents`. Any key you omit falls back to the built-in default, so you only need to override the pages you actually want to change. +Overrides for parameterized routes receive the route context (`{ params }`) as props, so wrap the ejected component in a small adapter that maps `params` to its expected props. + ### Blog ```tsx title="lib/stack-client.tsx" @@ -140,8 +142,8 @@ blogClientPlugin({ siteBasePath: "/pages", queryClient, pageComponents: { - posts: HomePageComponent, // published posts list - post: PostPageComponent, // single post detail + posts: HomePageComponent, // published posts list + post: ({ params }) => , // single post detail // drafts | newPost | editPost | tag — omit to keep defaults }, }) @@ -159,8 +161,11 @@ aiChatClientPlugin({ apiBasePath: "/api/data", queryClient, pageComponents: { - chat: ChatPageComponent, // chat home page - chatConversation: ChatConversationPageComponent, // conversation page (authenticated mode) + chat: ChatPageComponent, // chat home page + // conversation page (authenticated mode) + chatConversation: ({ params }) => ( + + ), }, }) ``` @@ -177,8 +182,11 @@ cmsClientPlugin({ apiBasePath: "/api/data", queryClient, pageComponents: { - dashboard: DashboardPageComponent, // CMS dashboard - contentList: ContentListPageComponent, // content list per type + dashboard: DashboardPageComponent, // CMS dashboard + // content list per type + contentList: ({ params }) => ( + + ), // newContent | editContent — omit to keep defaults }, }) @@ -197,7 +205,7 @@ formBuilderClientPlugin({ queryClient, pageComponents: { formList: FormListPageComponent, // form list page - editForm: EditFormPageComponent, // form editor + editForm: ({ params }) => , // form editor // newForm | submissions — omit to keep defaults }, }) @@ -216,7 +224,7 @@ uiBuilderClientPlugin({ queryClient, pageComponents: { pageList: PageListPageComponent, // page list - editPage: EditPagePageComponent, // page builder editor + editPage: ({ params }) => , // page builder editor // newPage — omit to keep default }, }) @@ -235,7 +243,7 @@ kanbanClientPlugin({ queryClient, pageComponents: { boards: BoardsPageComponent, // boards list - board: BoardPageComponent, // board detail + board: ({ params }) => , // board detail // newBoard — omit to keep default }, }) diff --git a/packages/stack/package.json b/packages/stack/package.json index d438f23d9..531f04341 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -1,6 +1,6 @@ { "name": "@btst/stack", - "version": "2.12.2", + "version": "3.0.0", "description": "A composable, plugin-based library for building full-stack applications.", "repository": { "type": "git", @@ -766,7 +766,7 @@ "@ai-sdk/react": ">=2.0.0", "@aws-sdk/client-s3": ">=3.0.0", "@aws-sdk/s3-request-presigner": ">=3.0.0", - "@btst/yar": ">=1.2.0", + "@btst/yar": ">=1.3.0", "@hookform/resolvers": ">=5.0.0", "@radix-ui/react-dialog": ">=1.1.0", "@radix-ui/react-label": ">=2.1.0", @@ -815,7 +815,7 @@ "@aws-sdk/client-s3": "^3.1011.0", "@aws-sdk/s3-request-presigner": "^3.1011.0", "@btst/adapter-memory": "2.2.2", - "@btst/yar": "1.2.0", + "@btst/yar": "1.3.0", "@types/react": "^19.0.0", "@types/slug": "^5.0.9", "@vercel/blob": "^0.27.3", diff --git a/packages/stack/src/__tests__/page-component-overrides.test.tsx b/packages/stack/src/__tests__/page-component-overrides.test.tsx index 548bfcc28..beaf90c26 100644 --- a/packages/stack/src/__tests__/page-component-overrides.test.tsx +++ b/packages/stack/src/__tests__/page-component-overrides.test.tsx @@ -1,47 +1,55 @@ import { describe, it, expect } from "vitest"; import { defineClientPlugin } from "../plugins/client"; -import type { ComponentType } from "react"; -import { createRoute } from "@btst/yar"; +import type { ComponentType, ReactElement } from "react"; +import { defineRoute, defineRoutes } from "@btst/yar"; /** * Default page components used by the mock plugin factory. * These stand in for the real plugin page components (e.g. HomePageComponent). */ -const DefaultListComponent: ComponentType = () =>
Default List
; +const DefaultListComponent = () =>
Default List
; const DefaultDetailComponent: ComponentType<{ id: string }> = ({ id }) => (
Default Detail {id}
); +/** + * Renders a bound page component (context injected as props) and returns the + * produced element for inspection, without needing a DOM. + */ +function renderBound( + Component: ComponentType> | undefined, +): ReactElement> | null { + if (!Component) return null; + return ( + Component as ( + props: Record, + ) => ReactElement> + )({}); +} + /** * Lightweight mock plugin factory that mirrors the real plugin pattern: - * - Accepts a config with an optional `pageComponents` field - * - Falls back to built-in components when no override is provided - * - For param routes, extracts the override before the handler closure + * - Declares routes with defineRoute + * - Applies the optional `pageComponents` config via defineRoutes' pages option */ function createTestPlugin(config: { pageComponents?: { list?: ComponentType; - detail?: ComponentType<{ id: string }>; + detail?: ComponentType<{ params: { id: string } }>; }; }) { return defineClientPlugin({ name: "test", - routes: () => ({ - list: createRoute("/items", () => { - const CustomList = config.pageComponents?.list; - return { - PageComponent: CustomList ?? DefaultListComponent, - }; - }), - detail: createRoute("/items/:id", ({ params }) => { - const CustomDetail = config.pageComponents?.detail; - return { - PageComponent: CustomDetail - ? () => - : () => , - }; - }), - }), + routes: () => + defineRoutes( + { + list: defineRoute("/items", { page: DefaultListComponent }), + detail: defineRoute("/items/:id", { + page: ({ params }) => , + }), + }, + { pages: config.pageComponents }, + ), }); } @@ -52,7 +60,8 @@ describe("pageComponents overrides", () => { const routes = plugin.routes(); const routeData = routes.list(); - expect(routeData.PageComponent).toBe(DefaultListComponent); + const element = renderBound(routeData.PageComponent); + expect(element?.type).toBe(DefaultListComponent); }); it("uses the default component wrapper for a param route", () => { @@ -60,14 +69,12 @@ describe("pageComponents overrides", () => { const routes = plugin.routes(); const routeData = routes.detail({ params: { id: "42" } }); - // Should be an inline wrapper, not the custom component expect(routeData.PageComponent).toBeDefined(); expect(typeof routeData.PageComponent).toBe("function"); - // Verify it is NOT the custom component (no override was given) - const CustomDetail: ComponentType<{ id: string }> = () => ( -
Custom
- ); - expect(routeData.PageComponent).not.toBe(CustomDetail); + + // The default page receives the route context and forwards params.id + const element = renderBound(routeData.PageComponent); + expect(element?.props.params).toEqual({ id: "42" }); }); }); @@ -81,8 +88,9 @@ describe("pageComponents overrides", () => { const routes = plugin.routes(); const routeData = routes.list(); - expect(routeData.PageComponent).toBe(CustomList); - expect(routeData.PageComponent).not.toBe(DefaultListComponent); + const element = renderBound(routeData.PageComponent); + expect(element?.type).toBe(CustomList); + expect(element?.type).not.toBe(DefaultListComponent); }); it("leaves other routes using their defaults when only one is overridden", () => { @@ -94,19 +102,21 @@ describe("pageComponents overrides", () => { const routes = plugin.routes(); // list uses custom - expect(routes.list().PageComponent).toBe(CustomList); + const listElement = renderBound(routes.list().PageComponent); + expect(listElement?.type).toBe(CustomList); - // detail still uses a wrapper (no override) + // detail still uses its default wrapper (no override) const detailData = routes.detail({ params: { id: "1" } }); - expect(detailData.PageComponent).not.toBe(CustomList); + const detailElement = renderBound(detailData.PageComponent); + expect(detailElement?.type).not.toBe(CustomList); }); }); describe("custom component replaces default (param route)", () => { - it("uses the override wrapper for a param route", () => { - const CustomDetail: ComponentType<{ id: string }> = ({ id }) => ( -
Custom Detail {id}
- ); + it("passes the route context to the override as props", () => { + const CustomDetail: ComponentType<{ params: { id: string } }> = ({ + params, + }) =>
Custom Detail {params.id}
; const plugin = createTestPlugin({ pageComponents: { detail: CustomDetail }, @@ -114,34 +124,30 @@ describe("pageComponents overrides", () => { const routes = plugin.routes(); const routeData = routes.detail({ params: { id: "42" } }); - // Should be an inline wrapper (not the raw CustomDetail directly) - expect(routeData.PageComponent).toBeDefined(); - expect(typeof routeData.PageComponent).toBe("function"); - // Should NOT be the default - expect(routeData.PageComponent).not.toBe(DefaultDetailComponent); + const element = renderBound(routeData.PageComponent); + expect(element?.type).toBe(CustomDetail); + expect(element?.props.params).toEqual({ id: "42" }); }); it("produces a different PageComponent than when no override is given", () => { - const CustomDetail: ComponentType<{ id: string }> = ({ id }) => ( -
Custom Detail {id}
- ); + const CustomDetail: ComponentType<{ params: { id: string } }> = ({ + params, + }) =>
Custom Detail {params.id}
; const defaultPlugin = createTestPlugin({}); const overridePlugin = createTestPlugin({ pageComponents: { detail: CustomDetail }, }); - const defaultRouteData = defaultPlugin - .routes() - .detail({ params: { id: "1" } }); - const overrideRouteData = overridePlugin - .routes() - .detail({ params: { id: "1" } }); - - // The wrapped component should be a different function reference - expect(overrideRouteData.PageComponent).not.toBe( - defaultRouteData.PageComponent, + const defaultElement = renderBound( + defaultPlugin.routes().detail({ params: { id: "1" } }).PageComponent, + ); + const overrideElement = renderBound( + overridePlugin.routes().detail({ params: { id: "1" } }).PageComponent, ); + + expect(overrideElement?.type).toBe(CustomDetail); + expect(defaultElement?.type).not.toBe(CustomDetail); }); }); }); diff --git a/packages/stack/src/client/components/compose.tsx b/packages/stack/src/client/components/compose.tsx index 7116a31ee..d001f1766 100644 --- a/packages/stack/src/client/components/compose.tsx +++ b/packages/stack/src/client/components/compose.tsx @@ -38,8 +38,13 @@ export function RouteRenderer({ onError: (error: Error, info: ErrorInfo) => void; props?: any; }) { - // Resolve route on the client where components are available - const route = router.getRoute(path); + // Resolve route on the client where components are available. + // Memoized so PageComponent keeps a stable identity across re-renders: + // getRoute() invokes the route handler, which produces new component + // references each call. Without the memo, React would treat every parent + // re-render as a component type change and remount the whole subtree + // (losing state and re-triggering Suspense). + const route = React.useMemo(() => router.getRoute(path), [router, path]); return ( ; + chatConversation?: ComponentType<{ params: { id: string } }>; }; } @@ -392,25 +392,24 @@ export const aiChatClientPlugin = (config: AiChatClientConfig) => { return defineClientPlugin({ name: "ai-chat", - routes: () => ({ - // Chat home - simple chat interface without history - chat: createRoute("/chat", () => { - const CustomChat = config.pageComponents?.chat; - return { - PageComponent: - CustomChat ?? - (() => ( + routes: () => + defineRoutes( + { + // Chat home - simple chat interface without history + chat: defineRoute("/chat", { + page: () => ( - )), - loader: createConversationsLoader(config), - meta: createChatHomeMeta(config), - }; - }), - }), + ), + loader: createConversationsLoader(config), + meta: createChatHomeMeta(config), + }), + }, + { pages: config.pageComponents }, + ), sitemap: async () => [], }); @@ -420,42 +419,37 @@ export const aiChatClientPlugin = (config: AiChatClientConfig) => { return defineClientPlugin({ name: "ai-chat", - routes: () => ({ - // Chat home - new conversation or list - chat: createRoute("/chat", () => { - const CustomChat = config.pageComponents?.chat; - return { - PageComponent: - CustomChat ?? - (() => ( + routes: () => + defineRoutes( + { + // Chat home - new conversation or list + chat: defineRoute("/chat", { + page: () => ( - )), - loader: createConversationsLoader(config), - meta: createChatHomeMeta(config), - }; - }), + ), + loader: createConversationsLoader(config), + meta: createChatHomeMeta(config), + }), - // Existing conversation - chatConversation: createRoute("/chat/:id", ({ params }) => { - const CustomConversation = config.pageComponents?.chatConversation; - return { - PageComponent: CustomConversation - ? () => - : () => ( - - ), - loader: createConversationLoader(params.id, config), - meta: createConversationMeta(params.id, config), - }; - }), - }), + // Existing conversation + chatConversation: defineRoute("/chat/:id", { + page: ({ params }) => ( + + ), + loader: ({ params }) => + createConversationLoader(params.id, config)(), + meta: ({ params }) => createConversationMeta(params.id, config)(), + }), + }, + { pages: config.pageComponents }, + ), // Chat pages typically shouldn't be in sitemap, but we provide the option sitemap: async () => { diff --git a/packages/stack/src/plugins/blog/client/plugin.tsx b/packages/stack/src/plugins/blog/client/plugin.tsx index d72d0698a..d620604b5 100644 --- a/packages/stack/src/plugins/blog/client/plugin.tsx +++ b/packages/stack/src/plugins/blog/client/plugin.tsx @@ -4,7 +4,7 @@ import { isConnectionError, runClientHookWithShim, } from "@btst/stack/plugins/client"; -import { createRoute } from "@btst/yar"; +import { defineRoute, defineRoutes } from "@btst/yar"; import type { ComponentType } from "react"; import type { QueryClient } from "@tanstack/react-query"; import { createSanitizedSSRLoaderError } from "../../utils"; @@ -91,6 +91,7 @@ export interface BlogClientConfig { * Optional page component overrides. * Replace any plugin page with a custom React component. * The built-in component is used as the fallback when not provided. + * Every override receives the route context (`params`, `query`) as props. */ pageComponents?: { /** Replaces the published posts list page */ @@ -100,11 +101,11 @@ export interface BlogClientConfig { /** Replaces the new post page */ newPost?: ComponentType; /** Replaces the single post page */ - post?: ComponentType<{ slug: string }>; + post?: ComponentType<{ params: { slug: string } }>; /** Replaces the edit post page */ - editPost?: ComponentType<{ slug: string }>; + editPost?: ComponentType<{ params: { slug: string } }>; /** Replaces the tag posts page */ - tag?: ComponentType<{ tagSlug: string }>; + tag?: ComponentType<{ params: { tagSlug: string } }>; }; } @@ -728,64 +729,47 @@ export const blogClientPlugin = (config: BlogClientConfig) => defineClientPlugin({ name: "blog", - routes: () => ({ - posts: createRoute("/blog", () => { - const CustomPosts = config.pageComponents?.posts; - return { - PageComponent: - CustomPosts ?? (() => ), - loader: createPostsLoader(true, config), - meta: createPostsListMeta(true, config), - }; - }), - drafts: createRoute("/blog/drafts", () => { - const CustomDrafts = config.pageComponents?.drafts; - return { - PageComponent: - CustomDrafts ?? (() => ), - loader: createPostsLoader(false, config), - meta: createPostsListMeta(false, config), - }; - }), - newPost: createRoute("/blog/new", () => { - const CustomNewPost = config.pageComponents?.newPost; - return { - PageComponent: CustomNewPost ?? NewPostPageComponent, - loader: createNewPostLoader(config), - meta: createNewPostMeta(config), - }; - }), - editPost: createRoute("/blog/:slug/edit", ({ params: { slug } }) => { - const CustomEditPost = config.pageComponents?.editPost; - return { - PageComponent: CustomEditPost - ? () => - : () => , - loader: createPostLoader(slug, config, `/blog/${slug}/edit`), - meta: createEditPostMeta(slug, config), - }; - }), - tag: createRoute("/blog/tag/:tagSlug", ({ params: { tagSlug } }) => { - const CustomTag = config.pageComponents?.tag; - return { - PageComponent: CustomTag - ? () => - : () => , - loader: createTagLoader(tagSlug, config), - meta: createTagMeta(tagSlug, config), - }; - }), - post: createRoute("/blog/:slug", ({ params: { slug } }) => { - const CustomPost = config.pageComponents?.post; - return { - PageComponent: CustomPost - ? () => - : () => , - loader: createPostLoader(slug, config), - meta: createPostMeta(slug, config), - }; - }), - }), + routes: () => + defineRoutes( + { + posts: defineRoute("/blog", { + page: () => , + loader: createPostsLoader(true, config), + meta: createPostsListMeta(true, config), + }), + drafts: defineRoute("/blog/drafts", { + page: () => , + loader: createPostsLoader(false, config), + meta: createPostsListMeta(false, config), + }), + newPost: defineRoute("/blog/new", { + page: NewPostPageComponent, + loader: createNewPostLoader(config), + meta: createNewPostMeta(config), + }), + editPost: defineRoute("/blog/:slug/edit", { + page: ({ params }) => , + loader: ({ params }) => + createPostLoader( + params.slug, + config, + `/blog/${params.slug}/edit`, + )(), + meta: ({ params }) => createEditPostMeta(params.slug, config)(), + }), + tag: defineRoute("/blog/tag/:tagSlug", { + page: ({ params }) => , + loader: ({ params }) => createTagLoader(params.tagSlug, config)(), + meta: ({ params }) => createTagMeta(params.tagSlug, config)(), + }), + post: defineRoute("/blog/:slug", { + page: ({ params }) => , + loader: ({ params }) => createPostLoader(params.slug, config)(), + meta: ({ params }) => createPostMeta(params.slug, config)(), + }), + }, + { pages: config.pageComponents }, + ), sitemap: async () => { const origin = `${config.siteBaseURL}${config.siteBasePath}`; diff --git a/packages/stack/src/plugins/client/index.ts b/packages/stack/src/plugins/client/index.ts index fda696f0e..775a5bfff 100644 --- a/packages/stack/src/plugins/client/index.ts +++ b/packages/stack/src/plugins/client/index.ts @@ -27,8 +27,13 @@ export { } from "../utils"; // Re-export Yar types needed for plugins -export type { Route } from "@btst/yar"; -export { createRoute, createRouter } from "@btst/yar"; +export type { Route, RouteContext, RouteDef } from "@btst/yar"; +export { + createRoute, + createRouter, + defineRoute, + defineRoutes, +} from "@btst/yar"; export { createClient } from "better-call/client"; diff --git a/packages/stack/src/plugins/cms/client/plugin.tsx b/packages/stack/src/plugins/cms/client/plugin.tsx index 0a343d055..19b67412c 100644 --- a/packages/stack/src/plugins/cms/client/plugin.tsx +++ b/packages/stack/src/plugins/cms/client/plugin.tsx @@ -5,7 +5,7 @@ import { isConnectionError, runClientHookWithShim, } from "@btst/stack/plugins/client"; -import { createRoute } from "@btst/yar"; +import { defineRoute, defineRoutes } from "@btst/yar"; import type { ComponentType } from "react"; import type { QueryClient } from "@tanstack/react-query"; import { createSanitizedSSRLoaderError } from "../../utils"; @@ -141,11 +141,11 @@ export interface CMSClientConfig { /** Replaces the CMS dashboard page */ dashboard?: ComponentType; /** Replaces the content list page */ - contentList?: ComponentType<{ typeSlug: string }>; + contentList?: ComponentType<{ params: { typeSlug: string } }>; /** Replaces the new content editor page */ - newContent?: ComponentType<{ typeSlug: string }>; + newContent?: ComponentType<{ params: { typeSlug: string } }>; /** Replaces the edit content editor page */ - editContent?: ComponentType<{ typeSlug: string; id: string }>; + editContent?: ComponentType<{ params: { typeSlug: string; id: string } }>; }; } @@ -516,56 +516,50 @@ export const cmsClientPlugin = (config: CMSClientConfig) => defineClientPlugin({ name: "cms", - routes: () => ({ - dashboard: createRoute("/cms", () => { - const CustomDashboard = config.pageComponents?.dashboard; - return { - PageComponent: CustomDashboard ?? (() => ), - loader: createDashboardLoader(config), - meta: createDashboardMeta(), - }; - }), - - contentList: createRoute("/cms/:typeSlug", ({ params }) => { - const CustomContentList = config.pageComponents?.contentList; - return { - PageComponent: CustomContentList - ? () => - : () => , - loader: createContentListLoader(params.typeSlug, config), - meta: createContentListMeta(params.typeSlug, config), - }; - }), - - newContent: createRoute("/cms/:typeSlug/new", ({ params }) => { - const CustomNewContent = config.pageComponents?.newContent; - return { - PageComponent: CustomNewContent - ? () => - : () => , - loader: createContentEditorLoader(params.typeSlug, undefined, config), - meta: createContentEditorMeta(params.typeSlug, undefined, config), - }; - }), - - editContent: createRoute("/cms/:typeSlug/:id", ({ params }) => { - const CustomEditContent = config.pageComponents?.editContent; - return { - PageComponent: CustomEditContent - ? () => ( - - ) - : () => ( - - ), - loader: createContentEditorLoader(params.typeSlug, params.id, config), - meta: createContentEditorMeta(params.typeSlug, params.id, config), - }; - }), - }), + routes: () => + defineRoutes( + { + dashboard: defineRoute("/cms", { + page: DashboardPageComponent, + loader: createDashboardLoader(config), + meta: createDashboardMeta(), + }), + + contentList: defineRoute("/cms/:typeSlug", { + page: ({ params }) => ( + + ), + loader: ({ params }) => + createContentListLoader(params.typeSlug, config)(), + meta: ({ params }) => + createContentListMeta(params.typeSlug, config)(), + }), + + newContent: defineRoute("/cms/:typeSlug/new", { + page: ({ params }) => ( + + ), + loader: ({ params }) => + createContentEditorLoader(params.typeSlug, undefined, config)(), + meta: ({ params }) => + createContentEditorMeta(params.typeSlug, undefined, config)(), + }), + + editContent: defineRoute("/cms/:typeSlug/:id", { + page: ({ params }) => ( + + ), + loader: ({ params }) => + createContentEditorLoader(params.typeSlug, params.id, config)(), + meta: ({ params }) => + createContentEditorMeta(params.typeSlug, params.id, config)(), + }), + }, + { pages: config.pageComponents }, + ), sitemap: async () => { // CMS admin pages should NOT be in sitemap diff --git a/packages/stack/src/plugins/comments/client/plugin.tsx b/packages/stack/src/plugins/comments/client/plugin.tsx index 25c72ce69..c2b4e266b 100644 --- a/packages/stack/src/plugins/comments/client/plugin.tsx +++ b/packages/stack/src/plugins/comments/client/plugin.tsx @@ -5,7 +5,7 @@ import { createApiClient, isConnectionError, } from "@btst/stack/plugins/client"; -import { createRoute } from "@btst/yar"; +import { defineRoute } from "@btst/yar"; import type { QueryClient } from "@tanstack/react-query"; import type { CommentsApiRouter } from "../api"; import { createCommentsQueryKeys } from "../query-keys"; @@ -251,8 +251,8 @@ export const commentsClientPlugin = (config: CommentsClientConfig) => name: "comments", routes: () => ({ - moderation: createRoute("/comments/moderation", () => ({ - PageComponent: ModerationPageComponent, + moderation: defineRoute("/comments/moderation", { + page: ModerationPageComponent, loader: createModerationLoader(config), meta: createCommentsRouteMeta( config, @@ -260,9 +260,9 @@ export const commentsClientPlugin = (config: CommentsClientConfig) => "Comment Moderation", "Review and manage comments across all resources.", ), - })), - userComments: createRoute("/comments", () => ({ - PageComponent: UserCommentsPageComponent, + }), + userComments: defineRoute("/comments", { + page: UserCommentsPageComponent, loader: createUserCommentsLoader(config), meta: createCommentsRouteMeta( config, @@ -270,6 +270,6 @@ export const commentsClientPlugin = (config: CommentsClientConfig) => "User Comments", "View and manage your comments across resources.", ), - })), + }), }), }); diff --git a/packages/stack/src/plugins/form-builder/client/plugin.tsx b/packages/stack/src/plugins/form-builder/client/plugin.tsx index a43839688..08160f71a 100644 --- a/packages/stack/src/plugins/form-builder/client/plugin.tsx +++ b/packages/stack/src/plugins/form-builder/client/plugin.tsx @@ -6,7 +6,7 @@ import { isConnectionError, runClientHookWithShim, } from "@btst/stack/plugins/client"; -import { createRoute } from "@btst/yar"; +import { defineRoute, defineRoutes } from "@btst/yar"; import type { ComponentType } from "react"; import type { QueryClient } from "@tanstack/react-query"; import { createSanitizedSSRLoaderError } from "../../utils"; @@ -140,9 +140,9 @@ export interface FormBuilderClientConfig { /** Replaces the new form page */ newForm?: ComponentType; /** Replaces the form editor page */ - editForm?: ComponentType<{ id: string }>; + editForm?: ComponentType<{ params: { id: string } }>; /** Replaces the form submissions page */ - submissions?: ComponentType<{ formId: string }>; + submissions?: ComponentType<{ params: { id: string } }>; }; } @@ -516,47 +516,39 @@ export const formBuilderClientPlugin = (config: FormBuilderClientConfig) => defineClientPlugin({ name: "form-builder", - routes: () => ({ - formList: createRoute("/forms", () => { - const CustomFormList = config.pageComponents?.formList; - return { - PageComponent: CustomFormList ?? (() => ), - loader: createFormListLoader(config), - meta: createFormListMeta(), - }; - }), - - newForm: createRoute("/forms/new", () => { - const CustomNewForm = config.pageComponents?.newForm; - return { - PageComponent: CustomNewForm ?? (() => ), - loader: createFormBuilderLoader(undefined, config), - meta: createFormBuilderMeta(undefined, config), - }; - }), - - editForm: createRoute("/forms/:id/edit", ({ params }) => { - const CustomEditForm = config.pageComponents?.editForm; - return { - PageComponent: CustomEditForm - ? () => - : () => , - loader: createFormBuilderLoader(params.id, config), - meta: createFormBuilderMeta(params.id, config), - }; - }), - - submissions: createRoute("/forms/:id/submissions", ({ params }) => { - const CustomSubmissions = config.pageComponents?.submissions; - return { - PageComponent: CustomSubmissions - ? () => - : () => , - loader: createSubmissionsLoader(params.id, config), - meta: createSubmissionsMeta(params.id, config), - }; - }), - }), + routes: () => + defineRoutes( + { + formList: defineRoute("/forms", { + page: FormListPageComponent, + loader: createFormListLoader(config), + meta: createFormListMeta(), + }), + + newForm: defineRoute("/forms/new", { + page: () => , + loader: createFormBuilderLoader(undefined, config), + meta: createFormBuilderMeta(undefined, config), + }), + + editForm: defineRoute("/forms/:id/edit", { + page: ({ params }) => , + loader: ({ params }) => + createFormBuilderLoader(params.id, config)(), + meta: ({ params }) => createFormBuilderMeta(params.id, config)(), + }), + + submissions: defineRoute("/forms/:id/submissions", { + page: ({ params }) => ( + + ), + loader: ({ params }) => + createSubmissionsLoader(params.id, config)(), + meta: ({ params }) => createSubmissionsMeta(params.id, config)(), + }), + }, + { pages: config.pageComponents }, + ), sitemap: async () => { // Form Builder admin pages should NOT be in sitemap diff --git a/packages/stack/src/plugins/kanban/client/plugin.tsx b/packages/stack/src/plugins/kanban/client/plugin.tsx index 67cdf3aad..93e3b054c 100644 --- a/packages/stack/src/plugins/kanban/client/plugin.tsx +++ b/packages/stack/src/plugins/kanban/client/plugin.tsx @@ -4,7 +4,7 @@ import { isConnectionError, runClientHookWithShim, } from "@btst/stack/plugins/client"; -import { createRoute } from "@btst/yar"; +import { defineRoute, defineRoutes } from "@btst/yar"; import type { ComponentType } from "react"; import type { QueryClient } from "@tanstack/react-query"; import type { KanbanApiRouter } from "../api"; @@ -92,7 +92,7 @@ export interface KanbanClientConfig { /** Replaces the new board page */ newBoard?: ComponentType; /** Replaces the board detail page */ - board?: ComponentType<{ boardId: string }>; + board?: ComponentType<{ params: { boardId: string } }>; }; } @@ -420,34 +420,29 @@ export const kanbanClientPlugin = (config: KanbanClientConfig) => defineClientPlugin({ name: "kanban", - routes: () => ({ - boards: createRoute("/kanban", () => { - const CustomBoards = config.pageComponents?.boards; - return { - PageComponent: CustomBoards ?? (() => ), - loader: createBoardsLoader(config), - meta: createBoardsListMeta(config), - }; - }), - newBoard: createRoute("/kanban/new", () => { - const CustomNewBoard = config.pageComponents?.newBoard; - return { - PageComponent: CustomNewBoard ?? NewBoardPageComponent, - loader: createNewBoardLoader(config), - meta: createNewBoardMeta(config), - }; - }), - board: createRoute("/kanban/:boardId", ({ params: { boardId } }) => { - const CustomBoard = config.pageComponents?.board; - return { - PageComponent: CustomBoard - ? () => - : () => , - loader: createBoardLoader(boardId, config), - meta: createBoardMeta(boardId, config), - }; - }), - }), + routes: () => + defineRoutes( + { + boards: defineRoute("/kanban", { + page: BoardsListPageComponent, + loader: createBoardsLoader(config), + meta: createBoardsListMeta(config), + }), + newBoard: defineRoute("/kanban/new", { + page: NewBoardPageComponent, + loader: createNewBoardLoader(config), + meta: createNewBoardMeta(config), + }), + board: defineRoute("/kanban/:boardId", { + page: ({ params }) => ( + + ), + loader: ({ params }) => createBoardLoader(params.boardId, config)(), + meta: ({ params }) => createBoardMeta(params.boardId, config)(), + }), + }, + { pages: config.pageComponents }, + ), sitemap: async () => { const origin = `${config.siteBaseURL}${config.siteBasePath}`; diff --git a/packages/stack/src/plugins/media/client/plugin.tsx b/packages/stack/src/plugins/media/client/plugin.tsx index df1fb7aa7..5c05de836 100644 --- a/packages/stack/src/plugins/media/client/plugin.tsx +++ b/packages/stack/src/plugins/media/client/plugin.tsx @@ -3,7 +3,7 @@ import { createApiClient, isConnectionError, } from "@btst/stack/plugins/client"; -import { createRoute } from "@btst/yar"; +import { defineRoute, defineRoutes } from "@btst/yar"; import type { ComponentType } from "react"; import type { QueryClient } from "@tanstack/react-query"; import { LibraryPageComponent } from "./components/pages/library-page"; @@ -81,16 +81,17 @@ export const mediaClientPlugin = (config: MediaClientConfig) => defineClientPlugin({ name: "media", - routes: () => ({ - library: createRoute("/media", () => { - const CustomLibrary = config.pageComponents?.library; - return { - PageComponent: CustomLibrary ?? LibraryPageComponent, - loader: createMediaLibraryLoader(config), - meta: createMediaLibraryMeta(config), - }; - }), - }), + routes: () => + defineRoutes( + { + library: defineRoute("/media", { + page: LibraryPageComponent, + loader: createMediaLibraryLoader(config), + meta: createMediaLibraryMeta(config), + }), + }, + { pages: config.pageComponents }, + ), }); function createMediaLibraryLoader(config: MediaClientConfig) { diff --git a/packages/stack/src/plugins/route-docs/client/plugin.tsx b/packages/stack/src/plugins/route-docs/client/plugin.tsx index 38a4674c7..71653ec6d 100644 --- a/packages/stack/src/plugins/route-docs/client/plugin.tsx +++ b/packages/stack/src/plugins/route-docs/client/plugin.tsx @@ -1,6 +1,6 @@ import { lazy } from "react"; import { defineClientPlugin } from "@btst/stack/plugins/client"; -import { createRoute } from "@btst/yar"; +import { defineRoute } from "@btst/yar"; import type { QueryClient } from "@tanstack/react-query"; import type { ClientStackContext } from "../../../types"; import { @@ -204,20 +204,18 @@ export const routeDocsClientPlugin = (config: RouteDocsClientConfig) => { moduleStoredContext = context || null; return { - docs: createRoute("/route-docs", () => { - return { - PageComponent: () => ( - - ), - LoadingComponent: () => , - ErrorComponent: () => , - loader: createRouteDocsLoader(config), - meta: createDocsMeta(config), - }; + docs: defineRoute("/route-docs", { + page: () => ( + + ), + loading: DocsPageSkeleton, + error: DocsErrorComponent, + loader: createRouteDocsLoader(config), + meta: createDocsMeta(config), }), }; }, diff --git a/packages/stack/src/plugins/ui-builder/client/plugin.tsx b/packages/stack/src/plugins/ui-builder/client/plugin.tsx index 304e477a5..ac94b54c0 100644 --- a/packages/stack/src/plugins/ui-builder/client/plugin.tsx +++ b/packages/stack/src/plugins/ui-builder/client/plugin.tsx @@ -6,7 +6,7 @@ import { isConnectionError, runClientHookWithShim, } from "@btst/stack/plugins/client"; -import { createRoute } from "@btst/yar"; +import { defineRoute, defineRoutes } from "@btst/yar"; import type { ComponentType } from "react"; import type { QueryClient } from "@tanstack/react-query"; import type { CMSApiRouter } from "../../cms/api"; @@ -63,7 +63,7 @@ export interface UIBuilderClientConfig { /** Replaces the new page builder page */ newPage?: ComponentType; /** Replaces the edit page builder page */ - editPage?: ComponentType<{ id: string }>; + editPage?: ComponentType<{ params: { id: string } }>; }; } @@ -334,36 +334,30 @@ export const uiBuilderClientPlugin = (config: UIBuilderClientConfig) => defineClientPlugin({ name: "ui-builder", - routes: () => ({ - pageList: createRoute("/ui-builder", () => { - const CustomPageList = config.pageComponents?.pageList; - return { - PageComponent: CustomPageList ?? (() => ), - loader: createPageListLoader(config), - meta: createPageListMeta(), - }; - }), + routes: () => + defineRoutes( + { + pageList: defineRoute("/ui-builder", { + page: PageListPageComponent, + loader: createPageListLoader(config), + meta: createPageListMeta(), + }), - newPage: createRoute("/ui-builder/new", () => { - const CustomNewPage = config.pageComponents?.newPage; - return { - PageComponent: CustomNewPage ?? (() => ), - loader: createPageBuilderLoader(undefined, config), - meta: createPageBuilderMeta(undefined, config), - }; - }), + newPage: defineRoute("/ui-builder/new", { + page: () => , + loader: createPageBuilderLoader(undefined, config), + meta: createPageBuilderMeta(undefined, config), + }), - editPage: createRoute("/ui-builder/:id/edit", ({ params }) => { - const CustomEditPage = config.pageComponents?.editPage; - return { - PageComponent: CustomEditPage - ? () => - : () => , - loader: createPageBuilderLoader(params.id, config), - meta: createPageBuilderMeta(params.id, config), - }; - }), - }), + editPage: defineRoute("/ui-builder/:id/edit", { + page: ({ params }) => , + loader: ({ params }) => + createPageBuilderLoader(params.id, config)(), + meta: ({ params }) => createPageBuilderMeta(params.id, config)(), + }), + }, + { pages: config.pageComponents }, + ), sitemap: async () => { // UI Builder admin pages should NOT be in sitemap diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5416205e0..cebefa757 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,345 +69,6 @@ importers: specifier: 'catalog:' version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.22.4)(yaml@2.8.2) - codegen-projects/nextjs: - dependencies: - '@ai-sdk/openai': - specifier: ^2.0.68 - version: 2.0.106(zod@4.4.3) - '@btst/adapter-memory': - specifier: ^2.2.2 - version: 2.2.2(2b2c34516092622e3b8ea86d6d757e06) - '@btst/stack': - specifier: workspace:* - version: link:../../packages/stack - '@tanstack/react-query': - specifier: ^5.90.2 - version: 5.90.10(react@19.2.7) - '@tanstack/react-query-devtools': - specifier: ^5.90.2 - version: 5.101.0(@tanstack/react-query@5.90.10(react@19.2.7))(react@19.2.7) - ai: - specifier: ^5.0.94 - version: 5.0.94(zod@4.4.3) - class-variance-authority: - specifier: ^0.7.1 - version: 0.7.1 - clsx: - specifier: ^2.1.1 - version: 2.1.1 - lucide-react: - specifier: ^0.545.0 - version: 0.545.0(react@19.2.7) - next: - specifier: 16.1.7 - version: 16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - next-themes: - specifier: ^0.4.6 - version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - radix-ui: - specifier: ^1.4.3 - version: 1.5.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: - specifier: 19.2.7 - version: 19.2.7 - react-dom: - specifier: 19.2.7 - version: 19.2.7(react@19.2.7) - shadcn: - specifier: ^4.2.0 - version: 4.11.0(typescript@5.9.3) - sonner: - specifier: ^2.0.7 - version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - tailwind-merge: - specifier: ^3.5.0 - version: 3.6.0 - tw-animate-css: - specifier: ^1.4.0 - version: 1.4.0 - zod: - specifier: 4.4.3 - version: 4.4.3 - devDependencies: - '@eslint/eslintrc': - specifier: ^3 - version: 3.3.5 - '@tailwindcss/postcss': - specifier: ^4.2.1 - version: 4.2.2 - '@types/node': - specifier: ^25.5.0 - version: 25.5.0 - '@types/react': - specifier: ^19.2.14 - version: 19.2.14 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.14) - eslint: - specifier: ^9.39.4 - version: 9.39.4(jiti@2.6.1) - eslint-config-next: - specifier: 16.1.7 - version: 16.1.7(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - postcss: - specifier: ^8 - version: 8.5.6 - prettier: - specifier: ^3.8.1 - version: 3.8.4 - prettier-plugin-tailwindcss: - specifier: ^0.7.2 - version: 0.7.4(prettier@3.8.4) - tailwindcss: - specifier: ^4.2.1 - version: 4.2.2 - typescript: - specifier: ^5.9.3 - version: 5.9.3 - - codegen-projects/react-router: - dependencies: - '@ai-sdk/openai': - specifier: ^2.0.68 - version: 2.0.106(zod@4.4.3) - '@btst/adapter-memory': - specifier: ^2.2.2 - version: 2.2.2(7282e51985b70ab8a4219751233dfac7) - '@btst/stack': - specifier: workspace:* - version: link:../../packages/stack - '@fontsource-variable/geist': - specifier: ^5.2.8 - version: 5.2.9 - '@react-router/node': - specifier: 7.13.1 - version: 7.13.1(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) - '@react-router/serve': - specifier: 7.13.1 - version: 7.13.1(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) - '@tanstack/react-query': - specifier: ^5.90.2 - version: 5.90.10(react@19.2.7) - '@tanstack/react-query-devtools': - specifier: ^5.90.2 - version: 5.101.0(@tanstack/react-query@5.90.10(react@19.2.7))(react@19.2.7) - ai: - specifier: ^5.0.94 - version: 5.0.94(zod@4.4.3) - class-variance-authority: - specifier: ^0.7.1 - version: 0.7.1 - clsx: - specifier: ^2.1.1 - version: 2.1.1 - isbot: - specifier: ^5.1.36 - version: 5.1.42 - lucide-react: - specifier: ^0.545.0 - version: 0.545.0(react@19.2.7) - next-themes: - specifier: ^0.4.6 - version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - radix-ui: - specifier: ^1.4.3 - version: 1.5.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: - specifier: 19.2.7 - version: 19.2.7 - react-dom: - specifier: 19.2.7 - version: 19.2.7(react@19.2.7) - react-router: - specifier: 7.13.1 - version: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - shadcn: - specifier: ^4.2.0 - version: 4.11.0(typescript@5.9.3) - sonner: - specifier: ^2.0.7 - version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - tailwind-merge: - specifier: ^3.5.0 - version: 3.6.0 - tw-animate-css: - specifier: ^1.4.0 - version: 1.4.0 - zod: - specifier: 4.4.3 - version: 4.4.3 - devDependencies: - '@react-router/dev': - specifier: 7.13.1 - version: 7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(tsx@4.22.4)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))(yaml@2.8.2) - '@tailwindcss/vite': - specifier: ^4.2.1 - version: 4.3.0(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - '@types/node': - specifier: ^22 - version: 22.19.20 - '@types/react': - specifier: ^19.2.14 - version: 19.2.14 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.14) - prettier: - specifier: ^3.8.1 - version: 3.8.4 - prettier-plugin-tailwindcss: - specifier: ^0.7.2 - version: 0.7.4(prettier@3.8.4) - tailwindcss: - specifier: ^4.2.1 - version: 4.2.2 - typescript: - specifier: ^5.9.3 - version: 5.9.3 - vite: - specifier: ^7.3.1 - version: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - vite-tsconfig-paths: - specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - - codegen-projects/tanstack: - dependencies: - '@ai-sdk/openai': - specifier: ^2.0.68 - version: 2.0.106(zod@4.4.3) - '@btst/adapter-memory': - specifier: ^2.2.2 - version: 2.2.2(61db3a5de70a4f07ceea932402bbed8f) - '@btst/stack': - specifier: workspace:* - version: link:../../packages/stack - '@fontsource-variable/geist': - specifier: ^5.2.9 - version: 5.2.9 - '@tailwindcss/vite': - specifier: ^4.2.1 - version: 4.3.0(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - '@tanstack/react-devtools': - specifier: latest - version: 0.10.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12) - '@tanstack/react-query': - specifier: ^5.90.2 - version: 5.90.10(react@19.2.7) - '@tanstack/react-query-devtools': - specifier: ^5.90.2 - version: 5.101.0(@tanstack/react-query@5.90.10(react@19.2.7))(react@19.2.7) - '@tanstack/react-router': - specifier: 1.168.10 - version: 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/react-router-devtools': - specifier: latest - version: 1.167.0(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.168.9)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/react-router-ssr-query': - specifier: 1.167.1 - version: 1.167.1(@tanstack/query-core@5.90.10)(@tanstack/react-query@5.90.10(react@19.2.7))(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.168.9)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/react-start': - specifier: 1.167.16 - version: 1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - '@tanstack/router-plugin': - specifier: 1.167.12 - version: 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - '@vitejs/plugin-react': - specifier: ^5.2.0 - version: 5.2.0(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - ai: - specifier: ^5.0.94 - version: 5.0.94(zod@4.4.3) - class-variance-authority: - specifier: ^0.7.1 - version: 0.7.1 - clsx: - specifier: ^2.1.1 - version: 2.1.1 - lucide-react: - specifier: ^0.545.0 - version: 0.545.0(react@19.2.7) - next-themes: - specifier: ^0.4.6 - version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - nitro: - specifier: 3.0.260603-beta - version: 3.0.260603-beta(@electric-sql/pglite@0.3.15)(@vercel/blob@0.27.3)(chokidar@4.0.3)(dotenv@17.2.3)(giget@2.0.0)(jiti@2.6.1)(lru-cache@11.5.1)(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(rollup@4.53.2)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - radix-ui: - specifier: ^1.5.0 - version: 1.5.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: - specifier: 19.2.7 - version: 19.2.7 - react-dom: - specifier: 19.2.7 - version: 19.2.7(react@19.2.7) - shadcn: - specifier: ^4.11.0 - version: 4.11.0(typescript@6.0.3) - sonner: - specifier: ^2.0.7 - version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - tailwind-merge: - specifier: ^3.6.0 - version: 3.6.0 - tailwindcss: - specifier: ^4 - version: 4.2.2 - tw-animate-css: - specifier: ^1.4.0 - version: 1.4.0 - vite: - specifier: 7.3.1 - version: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - vite-tsconfig-paths: - specifier: ^5.1.4 - version: 5.1.4(typescript@6.0.3)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - zod: - specifier: 4.4.3 - version: 4.4.3 - devDependencies: - '@tanstack/devtools-vite': - specifier: latest - version: 0.7.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - '@tanstack/eslint-config': - specifier: latest - version: 0.4.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@testing-library/dom': - specifier: ^10.4.1 - version: 10.4.1 - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@types/node': - specifier: ^22 - version: 22.19.20 - '@types/react': - specifier: ^19 - version: 19.2.14 - '@types/react-dom': - specifier: ^19 - version: 19.2.3(@types/react@19.2.14) - eslint: - specifier: ^9 - version: 9.39.4(jiti@2.6.1) - jsdom: - specifier: ^28 - version: 28.1.0(@noble/hashes@2.0.1) - prettier: - specifier: ^3.8.3 - version: 3.8.4 - prettier-plugin-tailwindcss: - specifier: ^0.8.0 - version: 0.8.0(prettier@3.8.4) - typescript: - specifier: ^6 - version: 6.0.3 - vitest: - specifier: ^4 - version: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@22.19.20)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@22.19.20)(typescript@6.0.3))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - docs: dependencies: '@btst/stack': @@ -439,7 +100,7 @@ importers: version: 0.522.0(react@19.2.7) next: specifier: 16.0.10 - version: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: specifier: 19.2.7 version: 19.2.7 @@ -543,7 +204,7 @@ importers: dependencies: '@btst/db': specifier: 2.2.2 - version: 2.2.2(6b742495bfb70b190049e28cee40c6a4) + version: 2.2.2(7010515aebf7f15e3f6fd94d16578921) '@hookform/resolvers': specifier: '>=5.0.0' version: 5.2.2(react-hook-form@7.66.1(react@19.2.7)) @@ -643,10 +304,10 @@ importers: version: 3.1011.0 '@btst/adapter-memory': specifier: 2.2.2 - version: 2.2.2(67378101110aa5c0ec2041bf2e8296f1) + version: 2.2.2(3f1048c33dcc3a34f5463c3b01c66883) '@btst/yar': - specifier: 1.2.0 - version: 1.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14) + specifier: 1.3.0 + version: 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react@19.2.7) '@types/react': specifier: ^19.0.0 version: 19.2.14 @@ -989,13 +650,13 @@ importers: version: 1.7.0(react@19.2.7) next: specifier: 16.0.10 - version: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) nuqs: specifier: ^2.8.9 - version: 2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) + version: 2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) react: specifier: 19.2.7 version: 19.2.7 @@ -1051,32 +712,16 @@ packages: peerDependencies: zod: 4.4.3 - '@ai-sdk/openai@2.0.106': - resolution: {integrity: sha512-EFC0rpo1wfe4HIz5KZCE72edP2J7fOeR7wPXzjCDljaTRB1wectKDIKRLowpU4F0mbcJ+XScAsoYNPK/Z20aVQ==} - engines: {node: '>=18'} - peerDependencies: - zod: 4.4.3 - '@ai-sdk/provider-utils@3.0.17': resolution: {integrity: sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw==} engines: {node: '>=18'} peerDependencies: zod: 4.4.3 - '@ai-sdk/provider-utils@3.0.25': - resolution: {integrity: sha512-CvsRu+32Y8a167s+lrIBtsybvgTHp8j9y+6BeTvLeoW3Q+okw/b4CnNUFOLIXsRaKHQKAH+IHNJPYWywfpw0LA==} - engines: {node: '>=18'} - peerDependencies: - zod: 4.4.3 - '@ai-sdk/provider@2.0.0': resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} engines: {node: '>=18'} - '@ai-sdk/provider@2.0.3': - resolution: {integrity: sha512-h88OPkavHTiN9tMn2l5awAznGB0lXzjcLhgR1/rvjB2zlLprsNxbM2tt6OJsHUxduLC3klq0/eqaSf6fX5XVww==} - engines: {node: '>=18'} - '@ai-sdk/react@2.0.94': resolution: {integrity: sha512-eVhV6O4uUn/aIckiRomSukovzMAqARsiLn3X0s92p/EpO+LGDixUcWsdw58hym6VQtM3r5f/qIYzhSFv6gnirQ==} engines: {node: '>=18'} @@ -1434,18 +1079,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.29.7': - resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-source@7.29.7': - resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.28.5': resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} engines: {node: '>=6.9.0'} @@ -1658,11 +1291,12 @@ packages: '@btst/db@2.2.2': resolution: {integrity: sha512-NLT9FXK4c60wP1DQ3lTRPd9Hqa+54VoqtQTT0w9xilk3Vm6DfNnWE9npwf8Nc+5noUBVtvgESUYssQHcOjNSbA==} - '@btst/yar@1.2.0': - resolution: {integrity: sha512-+pjP7tkARs8ENwq0mTGdXLtOD2IEcv4tAgN8tHkAWjmAmldbjeqNkZzSt9KJFhW6bZJEORtRQKnYI1vN152f5A==} + '@btst/yar@1.3.0': + resolution: {integrity: sha512-TD6/whPS6ES7uDFkL/1QIWSvrDyg7QDvtn9s7frAcGwbg4+0VNsC8DOhmJ7MlWb3qF5JuGQXM2hB62P4vSeWPQ==} peerDependencies: '@types/react': ^19.1.16 '@types/react-dom': ^19.1.9 + react: 19.2.7 '@chevrotain/cst-dts-gen@10.5.0': resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==} @@ -2363,51 +1997,14 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.2': - resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/config-helpers@0.4.2': - resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.17.0': - resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@2.1.4': resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/eslintrc@3.3.5': - resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@10.0.1': - resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - peerDependencies: - eslint: ^10.0.0 - peerDependenciesMeta: - eslint: - optional: true - '@eslint/js@8.57.1': resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/js@9.39.4': - resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.7': - resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.4.1': - resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@exodus/bytes@1.15.1': resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -2442,9 +2039,6 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - '@fontsource-variable/geist@5.2.9': - resolution: {integrity: sha512-TP+QSBG3wxKGPE33CbMy/L0Nu3qvJ6Fy81Yc4LnQ95xH+i+cfEp8fyU8/kfV14YwszxIFPhnoMTbjL71waVpyQ==} - '@formatjs/intl-localematcher@0.6.2': resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} @@ -2459,18 +2053,6 @@ packages: peerDependencies: react-hook-form: ^7.55.0 - '@humanfs/core@0.19.2': - resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.8': - resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} - engines: {node: '>=18.18.0'} - - '@humanfs/types@0.15.0': - resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} - engines: {node: '>=18.18.0'} - '@humanwhocodes/config-array@0.13.0': resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} engines: {node: '>=10.10.0'} @@ -2484,10 +2066,6 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead - '@humanwhocodes/retry@0.4.3': - resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} - engines: {node: '>=18.18'} - '@img/colour@1.0.0': resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} engines: {node: '>=18'} @@ -2835,9 +2413,6 @@ packages: '@milkdown/utils@7.17.1': resolution: {integrity: sha512-QTjbaxv+ZOB4a1BaQULkeJExyIvMnQw69UKf9QoM/E8iY2q1c8kppnN6i6ZeN9ZkCh4lXu+r7w/LH6zSFXrsdA==} - '@mjackson/node-fetch-server@0.2.0': - resolution: {integrity: sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng==} - '@modelcontextprotocol/sdk@1.29.0': resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} engines: {node: '>=18'} @@ -2883,9 +2458,6 @@ packages: '@next/eslint-plugin-next@15.3.4': resolution: {integrity: sha512-lBxYdj7TI8phbJcLSAqDt57nIcobEign5NYIKCiy0hXQhrUbTqLqOaSDi568U6vFg4hJfBdZYsG4iP/uKhCqgg==} - '@next/eslint-plugin-next@16.1.7': - resolution: {integrity: sha512-v/bRGOJlfRCO+NDKt0bZlIIWjhMKU8xbgEQBo+rV9C8S6czZvs96LZ/v24/GvpEnovZlL4QDpku/RzWHVbmPpA==} - '@next/swc-darwin-arm64@16.0.10': resolution: {integrity: sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg==} engines: {node: '>= 10'} @@ -3066,194 +2638,64 @@ packages: resolution: {integrity: sha512-scSmQBD8eANlMUOglxHrN1JdSW8tDghsPuS83otqealBiIeMukCQMOf/wc0JJjDXomqwNdEQFLXLGHrU6PGxuA==} engines: {node: '>= 20.0.0'} - '@oxc-parser/binding-android-arm-eabi@0.120.0': - resolution: {integrity: sha512-WU3qtINx802wOl8RxAF1v0VvmC2O4D9M8Sv486nLeQ7iPHVmncYZrtBhB4SYyX+XZxj2PNnCcN+PW21jHgiOxg==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-project/types@0.134.0': + resolution: {integrity: sha512-T0xuRRKrQFmocH8y+jGfpmSkGcheaJExY9lEihmR1Gm2aH+75B8CzgU2rABRQSzzDxLjZ15Sc0bRVLj5lVeNXQ==} + + '@oxc-resolver/binding-android-arm-eabi@11.19.1': + resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.120.0': - resolution: {integrity: sha512-SEf80EHdhlbjZEgzeWm0ZA/br4GKMenDW3QB/gtyeTV1gStvvZeFi40ioHDZvds2m4Z9J1bUAUL8yn1/+A6iGg==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-android-arm64@11.19.1': + resolution: {integrity: sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA==} cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.120.0': - resolution: {integrity: sha512-xVrrbCai8R8CUIBu3CjryutQnEYhZqs1maIqDvtUCFZb8vY33H7uh9mHpL3a0JBIKoBUKjPH8+rzyAeXnS2d6A==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-darwin-arm64@11.19.1': + resolution: {integrity: sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ==} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.120.0': - resolution: {integrity: sha512-xyHBbnJ6mydnQUH7MAcafOkkrNzQC6T+LXgDH/3InEq2BWl/g424IMRiJVSpVqGjB+p2bd0h0WRR8iIwzjU7rw==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-darwin-x64@11.19.1': + resolution: {integrity: sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ==} cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.120.0': - resolution: {integrity: sha512-UMnVRllquXUYTeNfFKmxTTEdZ/ix1nLl0ducDzMSREoWYGVIHnOOxoKMWlCOvRr9Wk/HZqo2rh1jeumbPGPV9A==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-freebsd-x64@11.19.1': + resolution: {integrity: sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw==} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': - resolution: {integrity: sha512-tkvn2CQ7QdcsMnpfiX3fd3wA3EFsWKYlcQzq9cFw/xc89Al7W6Y4O0FgLVkVQpo0Tnq/qtE1XfkJOnRRA9S/NA==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1': + resolution: {integrity: sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A==} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': - resolution: {integrity: sha512-WN5y135Ic42gQDk9grbwY9++fDhqf8knN6fnP+0WALlAUh4odY/BDK1nfTJRSfpJD9P3r1BwU0m3pW2DU89whQ==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-arm-musleabihf@11.19.1': + resolution: {integrity: sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ==} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.120.0': - resolution: {integrity: sha512-1GgQBCcXvFMw99EPdMy+4NZ3aYyXsxjf9kbUUg8HuAy3ZBXzOry5KfFEzT9nqmgZI1cuetvApkiJBZLAPo8uaw==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-arm64-gnu@11.19.1': + resolution: {integrity: sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig==} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.120.0': - resolution: {integrity: sha512-gmMQ70gsPdDBgpcErvJEoWNBr7bJooSLlvOBVBSGfOzlP5NvJ3bFvnUeZZ9d+dPrqSngtonf7nyzWUTUj/U+lw==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-arm64-musl@11.19.1': + resolution: {integrity: sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew==} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': - resolution: {integrity: sha512-T/kZuU0ajop0xhzVMwH5r3srC9Nqup5HaIo+3uFjIN5uPxa0LvSxC1ZqP4aQGJVW5G0z8/nCkjIfSMS91P/wzw==} - engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': + resolution: {integrity: sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': - resolution: {integrity: sha512-vn21KXLAXzaI3N5CZWlBr1iWeXLl9QFIMor7S1hUjUGTeUuWCoE6JZB040/ZNDwf+JXPX8Ao9KbmJq9FMC2iGw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@oxc-parser/binding-linux-riscv64-musl@0.120.0': - resolution: {integrity: sha512-SUbUxlar007LTGmSLGIC5x/WJvwhdX+PwNzFJ9f/nOzZOrCFbOT4ikt7pJIRg1tXVsEfzk5mWpGO1NFiSs4PIw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@oxc-parser/binding-linux-s390x-gnu@0.120.0': - resolution: {integrity: sha512-hYiPJTxyfJY2+lMBFk3p2bo0R9GN+TtpPFlRqVchL1qvLG+pznstramHNvJlw9AjaoRUHwp9IKR7UZQnRPGjgQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@oxc-parser/binding-linux-x64-gnu@0.120.0': - resolution: {integrity: sha512-q+5jSVZkprJCIy3dzJpApat0InJaoxQLsJuD6DkX8hrUS61z2lHQ1Fe9L2+TYbKHXCLWbL0zXe7ovkIdopBGMQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@oxc-parser/binding-linux-x64-musl@0.120.0': - resolution: {integrity: sha512-D9QDDZNnH24e7X4ftSa6ar/2hCavETfW3uk0zgcMIrZNy459O5deTbWrjGzZiVrSWigGtlQwzs2McBP0QsfV1w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@oxc-parser/binding-openharmony-arm64@0.120.0': - resolution: {integrity: sha512-TBU8ZwOUWAOUWVfmI16CYWbvh4uQb9zHnGBHsw5Cp2JUVG044OIY1CSHODLifqzQIMTXvDvLzcL89GGdUIqNrA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@oxc-parser/binding-wasm32-wasi@0.120.0': - resolution: {integrity: sha512-WG/FOZgDJCpJnuF3ToG/K28rcOmSY7FmFmfBKYb2fmLyhDzPpUldFGV7/Fz4ru0Iz/v4KPmf8xVgO8N3lO4KHA==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] - - '@oxc-parser/binding-win32-arm64-msvc@0.120.0': - resolution: {integrity: sha512-1T0HKGcsz/BKo77t7+89L8Qvu4f9DoleKWHp3C5sJEcbCjDOLx3m9m722bWZTY+hANlUEs+yjlK+lBFsA+vrVQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@oxc-parser/binding-win32-ia32-msvc@0.120.0': - resolution: {integrity: sha512-L7vfLzbOXsjBXV0rv/6Y3Jd9BRjPeCivINZAqrSyAOZN3moCopDN+Psq9ZrGNZtJzP8946MtlRFZ0Als0wBCOw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - - '@oxc-parser/binding-win32-x64-msvc@0.120.0': - resolution: {integrity: sha512-ys+upfqNtSu58huAhJMBKl3XCkGzyVFBlMlGPzHeFKgpFF/OdgNs1MMf8oaJIbgMH8ZxgGF7qfue39eJohmKIg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@oxc-project/types@0.120.0': - resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} - - '@oxc-project/types@0.134.0': - resolution: {integrity: sha512-T0xuRRKrQFmocH8y+jGfpmSkGcheaJExY9lEihmR1Gm2aH+75B8CzgU2rABRQSzzDxLjZ15Sc0bRVLj5lVeNXQ==} - - '@oxc-resolver/binding-android-arm-eabi@11.19.1': - resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==} - cpu: [arm] - os: [android] - - '@oxc-resolver/binding-android-arm64@11.19.1': - resolution: {integrity: sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA==} - cpu: [arm64] - os: [android] - - '@oxc-resolver/binding-darwin-arm64@11.19.1': - resolution: {integrity: sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ==} - cpu: [arm64] - os: [darwin] - - '@oxc-resolver/binding-darwin-x64@11.19.1': - resolution: {integrity: sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ==} - cpu: [x64] - os: [darwin] - - '@oxc-resolver/binding-freebsd-x64@11.19.1': - resolution: {integrity: sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw==} - cpu: [x64] - os: [freebsd] - - '@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1': - resolution: {integrity: sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A==} - cpu: [arm] - os: [linux] - - '@oxc-resolver/binding-linux-arm-musleabihf@11.19.1': - resolution: {integrity: sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ==} - cpu: [arm] - os: [linux] - - '@oxc-resolver/binding-linux-arm64-gnu@11.19.1': - resolution: {integrity: sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@oxc-resolver/binding-linux-arm64-musl@11.19.1': - resolution: {integrity: sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': - resolution: {integrity: sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': - resolution: {integrity: sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w==} + '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': + resolution: {integrity: sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w==} cpu: [riscv64] os: [linux] libc: [glibc] @@ -3463,15 +2905,9 @@ packages: '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} - '@radix-ui/number@1.1.2': - resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==} - '@radix-ui/primitive@1.1.3': resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} - '@radix-ui/primitive@1.1.4': - resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} - '@radix-ui/react-accessible-icon@1.1.7': resolution: {integrity: sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A==} peerDependencies: @@ -3485,19 +2921,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-accessible-icon@1.1.9': - resolution: {integrity: sha512-5W9KzJz/3DeYbGJHbZv8Q6AkxMOKUmALfc+PRg9dWwJZMk6zD37Sz8sZrF7UD6CBkiJvn7dNeRzn5G7XiCMyig==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-accordion@1.2.12': resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} peerDependencies: @@ -3511,8 +2934,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-accordion@1.2.13': - resolution: {integrity: sha512-xITxBB2p5m5tAe7M0F95kb4uAh7jSIKGlExMEm93HlW+XxZHV2eXFbPWLktd4JhRiwcnXNbO7iekcrbZy6ZCvA==} + '@radix-ui/react-alert-dialog@1.1.15': + resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3524,8 +2947,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-alert-dialog@1.1.15': - resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==} + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3537,8 +2960,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-alert-dialog@1.1.16': - resolution: {integrity: sha512-vPaIgo0mxYlvcFaM9jB2Uot9TjGXMuAPEvrc6BOLeV+I5U8s1dkIoouYaa6lmSfc5SPMo5x5djOTOTvaigdGMQ==} + '@radix-ui/react-aspect-ratio@1.1.7': + resolution: {integrity: sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3550,8 +2973,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-arrow@1.1.7': - resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + '@radix-ui/react-avatar@1.1.10': + resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3563,8 +2986,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-arrow@1.1.9': - resolution: {integrity: sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig==} + '@radix-ui/react-avatar@1.1.11': + resolution: {integrity: sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3576,8 +2999,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-aspect-ratio@1.1.7': - resolution: {integrity: sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==} + '@radix-ui/react-checkbox@1.3.3': + resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3589,8 +3012,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-aspect-ratio@1.1.9': - resolution: {integrity: sha512-Xy+Dpxt/5n9rVTdPrNFmf8GwG1NlT1pzCF/z1MgOGZMLZWdWl+km+ZRWGQAPEhbkzSwYEsfYmTca8NhUtVxqnw==} + '@radix-ui/react-collapsible@1.1.12': + resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3602,8 +3025,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-avatar@1.1.10': - resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3615,21 +3038,17 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-avatar@1.1.11': - resolution: {integrity: sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==} + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-avatar@1.1.12': - resolution: {integrity: sha512-NQCQyWC7QrDPhjMn8hUqFeU0lUrprIgm1AyMgLbzuQJibNnatdc3SSMo3/UGFu/eUkJUU1cEcKCnyhXTQzq6tA==} + '@radix-ui/react-context-menu@2.2.16': + resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3641,34 +3060,26 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-checkbox@1.3.3': - resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-checkbox@1.3.4': - resolution: {integrity: sha512-m3JmIOAX5ZzZ6VPjxEU2dbTOhoHi0nT5riwcDwe8idocsWf4a5DXJLDtZ6LfJwMBx7W+A2b7kp2TgPEKtaiF6A==} + '@radix-ui/react-context@1.1.3': + resolution: {integrity: sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-collapsible@1.1.12': - resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} + '@radix-ui/react-dialog@1.1.15': + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3680,21 +3091,17 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collapsible@1.1.13': - resolution: {integrity: sha512-F0s8+p2XNpfc3k02zBfB0jPWbkHVG162+p7BdUMyJ2308QMqZ+oaclX+FAzKFovgL5OqRU+Rvy6f/vbdlJVaqA==} + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-collection@1.1.7': - resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3706,8 +3113,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.9': - resolution: {integrity: sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ==} + '@radix-ui/react-dropdown-menu@2.1.16': + resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3719,8 +3126,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} peerDependencies: '@types/react': '*' react: 19.2.7 @@ -3728,17 +3135,21 @@ packages: '@types/react': optional: true - '@radix-ui/react-compose-refs@1.1.3': - resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: 19.2.7 + react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-context-menu@2.2.16': - resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==} + '@radix-ui/react-form@0.1.8': + resolution: {integrity: sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3750,8 +3161,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-context-menu@2.3.0': - resolution: {integrity: sha512-d7CouXhAW+CGmFOqmB+IEvd3E9GcaqfgvfjCc3hfulp2pkaUCEVEGa0SN5nNWYA+IvQ6g1Pt+S5dpNn1AoY9hg==} + '@radix-ui/react-hover-card@1.1.15': + resolution: {integrity: sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3763,17 +3174,13 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-context@1.1.2': - resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + '@radix-ui/react-icons@1.3.2': + resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} peerDependencies: - '@types/react': '*' react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-context@1.1.3': - resolution: {integrity: sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==} + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: '@types/react': '*' react: 19.2.7 @@ -3781,17 +3188,21 @@ packages: '@types/react': optional: true - '@radix-ui/react-context@1.1.4': - resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} + '@radix-ui/react-label@2.1.7': + resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: 19.2.7 + react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-dialog@1.1.15': - resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} + '@radix-ui/react-label@2.1.8': + resolution: {integrity: sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3803,8 +3214,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dialog@1.1.16': - resolution: {integrity: sha512-l9ok83YBclEZhbjgzt76Hw733e6cvRKPNgO6GJ/IETlufXG9p+fRu2wlvpImQvR6xdJ8h7J8J2DBvsPEiEsKMw==} + '@radix-ui/react-menu@2.1.16': + resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3816,26 +3227,34 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-direction@1.1.1': - resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + '@radix-ui/react-menubar@1.1.16': + resolution: {integrity: sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: 19.2.7 + react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-direction@1.1.2': - resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} + '@radix-ui/react-navigation-menu@1.2.14': + resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: 19.2.7 + react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-dismissable-layer@1.1.11': - resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + '@radix-ui/react-one-time-password-field@0.1.8': + resolution: {integrity: sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3847,8 +3266,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dismissable-layer@1.1.12': - resolution: {integrity: sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg==} + '@radix-ui/react-password-toggle-field@0.1.3': + resolution: {integrity: sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3860,8 +3279,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.16': - resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} + '@radix-ui/react-popover@1.1.15': + resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3873,8 +3292,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.17': - resolution: {integrity: sha512-S6b3Jm57sY5EdDyOMLkacbB0qMnKhy1RCKZCt795ZkmtUOAvojYIZ5p7dXHIh5Cyr3jCLLI5/g64V3FKLudZmw==} + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3886,39 +3305,34 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-focus-guards@1.1.3': - resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: 19.2.7 + react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-focus-guards@1.1.4': - resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-focus-scope@1.1.7': - resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 + react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - '@radix-ui/react-focus-scope@1.1.9': - resolution: {integrity: sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ==} + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3930,8 +3344,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-form@0.1.8': - resolution: {integrity: sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ==} + '@radix-ui/react-primitive@2.1.4': + resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3943,8 +3357,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-form@0.1.9': - resolution: {integrity: sha512-eTPyThIKDacJ3mJDvYwf/PSmsEYlOyA2Qcb+aGyWwYv+P5w57VPUkMVA2XJ9z0Du2KBY1HoHQzhPV9iYL/r4hg==} + '@radix-ui/react-progress@1.1.7': + resolution: {integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3956,8 +3370,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-hover-card@1.1.15': - resolution: {integrity: sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==} + '@radix-ui/react-radio-group@1.3.8': + resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3969,8 +3383,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-hover-card@1.1.16': - resolution: {integrity: sha512-hAileDBtd6CX7nlZOarOnISQ6PP4q0e16BX51ulzdZ+7IzjL0sDTVpFdmSYrIjw6zVNsfQBao5gG6AWr3qwfvA==} + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3982,31 +3396,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-icons@1.3.2': - resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} - peerDependencies: - react: 19.2.7 - - '@radix-ui/react-id@1.1.1': - resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-id@1.1.2': - resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-label@2.1.7': - resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} + '@radix-ui/react-scroll-area@1.2.10': + resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4018,8 +3409,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-label@2.1.8': - resolution: {integrity: sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==} + '@radix-ui/react-select@2.2.6': + resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4031,8 +3422,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-label@2.1.9': - resolution: {integrity: sha512-rDoTeMbCwRVcnmo7NGT9IlPo1yXmEI+xc1URP3oeewwZEV4mdTp1dYUhYbQdo4D1q2SjKVvv4N1gNY77QAQtjA==} + '@radix-ui/react-separator@1.1.7': + resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4044,8 +3435,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menu@2.1.16': - resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} + '@radix-ui/react-separator@1.1.8': + resolution: {integrity: sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4057,8 +3448,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menu@2.1.17': - resolution: {integrity: sha512-fmbNnFyf+JYCN0DhhWnEdUTDnZD1mXaPQWivdsPIb8oOSbARfD3LIQJbLCG8a8QLCwoMxiJ7GVPIFcC8Dw8v2Q==} + '@radix-ui/react-slider@1.3.6': + resolution: {integrity: sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4070,34 +3461,26 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menubar@1.1.16': - resolution: {integrity: sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==} + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-menubar@1.1.17': - resolution: {integrity: sha512-AKtZ4O782yO7qwIyq73WpulYt1IHhQ0htDb6wNcxzxnSDCcSWMVBiU9ycpcA90XzQO4IVIxIErtak6Kg/Vt0rQ==} + '@radix-ui/react-slot@1.2.4': + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-navigation-menu@1.2.14': - resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==} + '@radix-ui/react-switch@1.2.6': + resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4109,8 +3492,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-navigation-menu@1.2.15': - resolution: {integrity: sha512-/fS8hKCcRt4DwCGa5QIB3juRXmfYSOk4a2AEe/BDIyy7Hm+eje2Y13oUx5zejl+wFt1owrM7E8NWlbaEl5EGpg==} + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4122,8 +3505,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-one-time-password-field@0.1.8': - resolution: {integrity: sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg==} + '@radix-ui/react-toast@1.2.15': + resolution: {integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4135,8 +3518,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-one-time-password-field@0.1.9': - resolution: {integrity: sha512-fvCzA9hm7yN5xxTPJIi4VhSmH5gv+76ILsxguBK3cm3icD5BR4vW7POQmu8Zio0yh91uuouG/Kang40IbMkaSQ==} + '@radix-ui/react-toggle-group@1.1.11': + resolution: {integrity: sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4148,8 +3531,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-password-toggle-field@0.1.3': - resolution: {integrity: sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw==} + '@radix-ui/react-toggle@1.1.10': + resolution: {integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4161,8 +3544,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-password-toggle-field@0.1.4': - resolution: {integrity: sha512-qoDSkObZ9faJlsjlwyBH6ia7kq9vaJ2QwWTowT3nQpzPvUTAKesmWuGJYpd91HIoJqS+5ZPXy5uFPp+HlwdaAg==} + '@radix-ui/react-toolbar@1.1.11': + resolution: {integrity: sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4174,8 +3557,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popover@1.1.15': - resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} + '@radix-ui/react-tooltip@1.2.8': + resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4187,125 +3570,89 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popover@1.1.16': - resolution: {integrity: sha512-8brVpAU5Uq7Bh0c8EFc4ZTf2JJTYn0o+1L+CUJB3UYIOkTjKGMgoHvduylrahdmNlr3DfH0rFq2DrbNZXgaspw==} + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-popper@1.2.8': - resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-popper@1.3.0': - resolution: {integrity: sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ==} + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-portal@1.1.11': - resolution: {integrity: sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw==} + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-portal@1.1.9': - resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + '@radix-ui/react-use-is-hydrated@0.1.0': + resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-presence@1.1.6': - resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-primitive@2.1.3': - resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-primitive@2.1.4': - resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: 19.2.7 - react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-primitive@2.1.5': - resolution: {integrity: sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==} + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4317,685 +3664,35 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-progress@1.1.7': - resolution: {integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - '@radix-ui/react-progress@1.1.9': - resolution: {integrity: sha512-+EOkvg1Zn1vI1+fRDfRSAiJ7BWfcDAo5ASMmbqrcLZ4s4USk2FGkoHgeb2X+CkUgo2zJMiyObwf1k44CrRWsyw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@remirror/core-constants@3.0.0': + resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} - '@radix-ui/react-radio-group@1.3.8': - resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@rolldown/binding-android-arm64@1.1.0': + resolution: {integrity: sha512-gCYzGOSkYY6Z034suzd20euvds7lPzMEEla62DJGE/ZAlR4OMBnNbvnBSsIGUCAr52gaWMsloGxP4tVGtN5aCA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] - '@radix-ui/react-radio-group@1.4.0': - resolution: {integrity: sha512-eHdV5bLx9sH+tBnbDjkIBdvQEH/c6MEtQYhTbxkaDK9qsIFFLtmJYEQFVdwhnruWotLfQmIuWEL/J+L3utE8rQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@rolldown/binding-darwin-arm64@1.1.0': + resolution: {integrity: sha512-JQBD77MNgu+4Z6RAyg69acugdrhhVoWesr3l47zohYZ2YV2fwkWMArkN/2p4l6Ei+Sno7W5q+UsKdVWq5Ens0w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] - '@radix-ui/react-roving-focus@1.1.11': - resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@rolldown/binding-darwin-x64@1.1.0': + resolution: {integrity: sha512-p/8cXUTK4Sob604e+xxPhVSbDFf29E6J0l/xESM9rdCfn3aDai3nEs6TnMHUsdD5aNlFz0+gDbiGlozLKGa2YA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] - '@radix-ui/react-roving-focus@1.1.12': - resolution: {integrity: sha512-FvgPt1bRmg8Xt2QpF7NUZW3dE0ZQHGm41dAdgT2J2GJPoIXz+9Em3NobAxf4fupcxhgHu03E5CRiU2MWvObXyg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-scroll-area@1.2.10': - resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-scroll-area@1.2.11': - resolution: {integrity: sha512-DS39ziOgea75U/TrXKU2/oKp0be2jrDHnzFLvahg/0iNAT1Zq16e4Uw0WXwyXvsK+mG3BRyMb7A3NRZMDuEXtQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-select@2.2.6': - resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-select@2.3.0': - resolution: {integrity: sha512-mENc7WpJvJcW8hlMpzfFcHcEhTvYS5JMBmi9HVC1Q00uhBwML086MHYUV8QQdQv6lcu0Wg8dzd1RB8AFADcG/g==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-separator@1.1.7': - resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-separator@1.1.8': - resolution: {integrity: sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-separator@1.1.9': - resolution: {integrity: sha512-gvgW+JV/Mbjj6darztTetnmElpQEzZrXpJvfj+dOxNAxiyHEAyUvEjjl4zxblvmjmKmi3jfPoy7ZdxzCuUBJSA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-slider@1.3.6': - resolution: {integrity: sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-slider@1.4.0': - resolution: {integrity: sha512-RHcPlLOThRJM51DSIC33ZnpDEBYhyEFroVWkd2P54PGGjkmAt14RboYUU9E1MFst666zFHM0tGtWvMjSOtU1pw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-slot@1.2.3': - resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-slot@1.2.4': - resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-slot@1.2.5': - resolution: {integrity: sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-switch@1.2.6': - resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-switch@1.3.0': - resolution: {integrity: sha512-GP1EZwhoZO/GGnhM1P5/2Vpm8iN8EnngyU0oezn2l78kN8tj25pyrvjIaT7azBhK615KSt+P2w39y57YV5jVkA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-tabs@1.1.13': - resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-tabs@1.1.14': - resolution: {integrity: sha512-D5jwp9JNuwDeCw3CYD2Fz+sSHo0droQjC8u75dJHe4aWr5q6yBiXZU+hurXnKudRgEpUkD5TsI6bjHPo5ThUxA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toast@1.2.15': - resolution: {integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toast@1.2.16': - resolution: {integrity: sha512-WUymDDiN2DpoGudRN1aW4wF5O3BNQjZZO/5nngPoNiEVqjyOzirvZZNO0R6dC1ifucSINVaSv8JX1aq47VGgiA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toggle-group@1.1.11': - resolution: {integrity: sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toggle-group@1.1.12': - resolution: {integrity: sha512-TEgECgJaWGAHJJZGzNNEYTNBdIXqX7LchANycpyP7DkfjmuiSN7ISt1k/ZRGVJgVJonsgP4vwaiKMn5utrcwWQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toggle@1.1.10': - resolution: {integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toggle@1.1.11': - resolution: {integrity: sha512-FikrKJemoBGZQ6uRID0HJqSPBP6D7OppdD2OhLl0ZYLlAyPXI7MezoYGmumwNkrAoRm35xXkb4C8JPfJZZzcaw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toolbar@1.1.11': - resolution: {integrity: sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toolbar@1.1.12': - resolution: {integrity: sha512-4wHtJVdIgqMmEwUvxA0BYg/2JMRbt0L3+8UD8Ml/nhKkfXtiZcM8u/S15gQ5xj9YEd/0qlrm5bE805LsjQ+J8A==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-tooltip@1.2.8': - resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-tooltip@1.2.9': - resolution: {integrity: sha512-u6F9MmTtBSLkiXNVDrtB/yPCZarM9smNswC24YYLV/M+bth6J3Gs3vlJezEoFwKZvPvxhCpUYdUnOsNG/0XOlA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-callback-ref@1.1.2': - resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-controllable-state@1.2.2': - resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-controllable-state@1.2.3': - resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-effect-event@0.0.2': - resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-effect-event@0.0.3': - resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-escape-keydown@1.1.2': - resolution: {integrity: sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-is-hydrated@0.1.0': - resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-is-hydrated@0.1.1': - resolution: {integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.2': - resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-previous@1.1.1': - resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-previous@1.1.2': - resolution: {integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-rect@1.1.1': - resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-rect@1.1.2': - resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-size@1.1.1': - resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-size@1.1.2': - resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-visually-hidden@1.2.3': - resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-visually-hidden@1.2.5': - resolution: {integrity: sha512-tPcHNI3FajdDBFpl/Ez1m2WL0ufJqBKyHxMDBvKitopamK36WwBGOMicuMEZKkM5Wce41QxUyv6BsiqfrWBiGg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/rect@1.1.1': - resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - - '@radix-ui/rect@1.1.2': - resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} - - '@react-router/dev@7.13.1': - resolution: {integrity: sha512-H+kEvbbOaWGaitOyL6CgqPsHqRUh66HuVRvIEaZEqdoAY/1xChdhmmq6ZumMHzcFHgHlfOcoXgNHlz6ZO4NWcg==} - engines: {node: '>=20.0.0'} - hasBin: true - peerDependencies: - '@react-router/serve': ^7.13.1 - '@vitejs/plugin-rsc': ~0.5.7 - react-router: ^7.13.1 - react-server-dom-webpack: ^19.2.3 - typescript: ^5.1.0 - vite: ^5.1.0 || ^6.0.0 || ^7.0.0 - wrangler: ^3.28.2 || ^4.0.0 - peerDependenciesMeta: - '@react-router/serve': - optional: true - '@vitejs/plugin-rsc': - optional: true - react-server-dom-webpack: - optional: true - typescript: - optional: true - wrangler: - optional: true - - '@react-router/express@7.13.1': - resolution: {integrity: sha512-ujHom4LiEWsbnohNArwNT86QP3WRB5p+rY8AAll6s4gdrzgOXIy3FHDc3up5Lz8juUrZKh0d+B+PZa/IdDSK3A==} - engines: {node: '>=20.0.0'} - peerDependencies: - express: ^4.17.1 || ^5 - react-router: 7.13.1 - typescript: ^5.1.0 - peerDependenciesMeta: - typescript: - optional: true - - '@react-router/node@7.13.1': - resolution: {integrity: sha512-IWPPf+Q3nJ6q4bwyTf5leeGUfg8GAxSN1RKj5wp9SK915zKK+1u4TCOfOmr8hmC6IW1fcjKV0WChkM0HkReIiw==} - engines: {node: '>=20.0.0'} - peerDependencies: - react-router: 7.13.1 - typescript: ^5.1.0 - peerDependenciesMeta: - typescript: - optional: true - - '@react-router/serve@7.13.1': - resolution: {integrity: sha512-vh5lr41rioXLz/zNLTYo0zq4yh97AkgEkJK7bhPeXnNbLNtI36WCZ2AeBtSJ4sdx4gx5LZvcjP8zoWFfSbNupA==} - engines: {node: '>=20.0.0'} - hasBin: true - peerDependencies: - react-router: 7.13.1 - - '@remirror/core-constants@3.0.0': - resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} - - '@remix-run/node-fetch-server@0.13.3': - resolution: {integrity: sha512-UfjOXed/DQteaM5VyTfqTeGpHwyL2J5aoRGY6cydip4tt1ehNNeSwuXCC7AEGE0RWBs/7bgKxYkL/B/+UDe4AA==} - - '@rolldown/binding-android-arm64@1.1.0': - resolution: {integrity: sha512-gCYzGOSkYY6Z034suzd20euvds7lPzMEEla62DJGE/ZAlR4OMBnNbvnBSsIGUCAr52gaWMsloGxP4tVGtN5aCA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@rolldown/binding-darwin-arm64@1.1.0': - resolution: {integrity: sha512-JQBD77MNgu+4Z6RAyg69acugdrhhVoWesr3l47zohYZ2YV2fwkWMArkN/2p4l6Ei+Sno7W5q+UsKdVWq5Ens0w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@rolldown/binding-darwin-x64@1.1.0': - resolution: {integrity: sha512-p/8cXUTK4Sob604e+xxPhVSbDFf29E6J0l/xESM9rdCfn3aDai3nEs6TnMHUsdD5aNlFz0+gDbiGlozLKGa2YA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@rolldown/binding-freebsd-x64@1.1.0': - resolution: {integrity: sha512-KbtOSlVv6fElujiZWMcC3aQYhEwLVVf073RcwlSmpGQvIsKZFUqc0ef4sjUuurRwfbiI6JJXji9DQn+86hawmQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] + '@rolldown/binding-freebsd-x64@1.1.0': + resolution: {integrity: sha512-KbtOSlVv6fElujiZWMcC3aQYhEwLVVf073RcwlSmpGQvIsKZFUqc0ef4sjUuurRwfbiI6JJXji9DQn+86hawmQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] '@rolldown/binding-linux-arm-gnueabihf@1.1.0': resolution: {integrity: sha512-9fZ9i0o0/MQaw7om6Z6TsT7tfCk0jtbEFtC+aPqZL5RNsGWNcHvn6EHgL3dAprjq+AZzPTAQjg2JtpJaMt+6pg==} @@ -5071,9 +3768,6 @@ packages: '@rolldown/pluginutils@1.0.0-beta.40': resolution: {integrity: sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w==} - '@rolldown/pluginutils@1.0.0-rc.3': - resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} - '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} @@ -5508,36 +4202,6 @@ packages: resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} engines: {node: '>=18.0.0'} - '@solid-primitives/event-listener@2.4.5': - resolution: {integrity: sha512-nwRV558mIabl4yVAhZKY8cb6G+O1F0M6Z75ttTu5hk+SxdOnKSGj+eetDIu7Oax1P138ZdUU01qnBPR8rnxaEA==} - peerDependencies: - solid-js: ^1.6.12 - - '@solid-primitives/keyboard@1.3.5': - resolution: {integrity: sha512-sav+l+PL+74z3yaftVs7qd8c2SXkqzuxPOVibUe5wYMt+U5Hxp3V3XCPgBPN2I6cANjvoFtz0NiU8uHVLdi9FQ==} - peerDependencies: - solid-js: ^1.6.12 - - '@solid-primitives/resize-observer@2.1.5': - resolution: {integrity: sha512-AiyTknKcNBaKHbcSMuxtSNM8FjIuiSuFyFghdD0TcCMU9hKi9EmsC5pjfjDwxE+5EueB1a+T/34PLRI5vbBbKw==} - peerDependencies: - solid-js: ^1.6.12 - - '@solid-primitives/rootless@1.5.3': - resolution: {integrity: sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA==} - peerDependencies: - solid-js: ^1.6.12 - - '@solid-primitives/static-store@0.1.3': - resolution: {integrity: sha512-uxez7SXnr5GiRnzqO2IEDjOJRIXaG+0LZLBizmUA1FwSi+hrpuMzVBwyk70m4prcl8X6FDDXUl9O8hSq8wHbBQ==} - peerDependencies: - solid-js: ^1.6.12 - - '@solid-primitives/utils@6.4.0': - resolution: {integrity: sha512-AeGTBg8Wtkh/0s+evyLtP8piQoS4wyqqQaAFs2HJcFMMjYAtUgo+ZPduRXLjPlqKVc2ejeR544oeqpbn8Egn8A==} - peerDependencies: - solid-js: ^1.6.12 - '@stackblitz/sdk@1.11.0': resolution: {integrity: sha512-DFQGANNkEZRzFk1/rDP6TcFdM82ycHE+zfl9C/M/jXlH68jiqHWHFMQURLELoD8koxvu/eW5uhg94NSAZlYrUQ==} @@ -5550,29 +4214,14 @@ packages: '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - '@stylistic/eslint-plugin@5.10.0': - resolution: {integrity: sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^9.0.0 || ^10.0.0 - '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} '@tailwindcss/node@4.2.2': resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} - '@tailwindcss/node@4.3.0': - resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==} - '@tailwindcss/oxide-android-arm64@4.2.2': - resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [android] - - '@tailwindcss/oxide-android-arm64@4.3.0': - resolution: {integrity: sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==} + resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} engines: {node: '>= 20'} cpu: [arm64] os: [android] @@ -5583,48 +4232,24 @@ packages: cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-arm64@4.3.0': - resolution: {integrity: sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.2.2': resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.3.0': - resolution: {integrity: sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==} - engines: {node: '>= 20'} - cpu: [x64] - os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.2.2': resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-freebsd-x64@4.3.0': - resolution: {integrity: sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==} - engines: {node: '>= 20'} - cpu: [x64] - os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} engines: {node: '>= 20'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': - resolution: {integrity: sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==} - engines: {node: '>= 20'} - cpu: [arm] - os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} engines: {node: '>= 20'} @@ -5632,13 +4257,6 @@ packages: os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': - resolution: {integrity: sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@tailwindcss/oxide-linux-arm64-musl@4.2.2': resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} engines: {node: '>= 20'} @@ -5646,13 +4264,6 @@ packages: os: [linux] libc: [musl] - '@tailwindcss/oxide-linux-arm64-musl@4.3.0': - resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [linux] - libc: [musl] - '@tailwindcss/oxide-linux-x64-gnu@4.2.2': resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} engines: {node: '>= 20'} @@ -5660,13 +4271,6 @@ packages: os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-x64-gnu@4.3.0': - resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==} - engines: {node: '>= 20'} - cpu: [x64] - os: [linux] - libc: [glibc] - '@tailwindcss/oxide-linux-x64-musl@4.2.2': resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} engines: {node: '>= 20'} @@ -5674,13 +4278,6 @@ packages: os: [linux] libc: [musl] - '@tailwindcss/oxide-linux-x64-musl@4.3.0': - resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==} - engines: {node: '>= 20'} - cpu: [x64] - os: [linux] - libc: [musl] - '@tailwindcss/oxide-wasm32-wasi@4.2.2': resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} engines: {node: '>=14.0.0'} @@ -5693,50 +4290,22 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-wasm32-wasi@4.3.0': - resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] - bundledDependencies: - - '@napi-rs/wasm-runtime' - - '@emnapi/core' - - '@emnapi/runtime' - - '@tybys/wasm-util' - - '@emnapi/wasi-threads' - - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': - resolution: {integrity: sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.2.2': resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} engines: {node: '>= 20'} cpu: [x64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.3.0': - resolution: {integrity: sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==} - engines: {node: '>= 20'} - cpu: [x64] - os: [win32] - '@tailwindcss/oxide@4.2.2': resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} engines: {node: '>= 20'} - '@tailwindcss/oxide@4.3.0': - resolution: {integrity: sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==} - engines: {node: '>= 20'} - '@tailwindcss/postcss@4.2.2': resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==} @@ -5745,50 +4314,6 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' - '@tailwindcss/vite@4.3.0': - resolution: {integrity: sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==} - peerDependencies: - vite: ^5.2.0 || ^6 || ^7 || ^8 - - '@tanstack/devtools-client@0.0.6': - resolution: {integrity: sha512-f85ZJXJnDIFOoykG/BFIixuAevJovCvJF391LPs6YjBAPhGYC50NWlx1y4iF/UmK5/cCMx+/JqI5SBOz7FanQQ==} - engines: {node: '>=18'} - - '@tanstack/devtools-event-bus@0.4.1': - resolution: {integrity: sha512-cNnJ89Q021Zf883rlbBTfsaxTfi2r73/qejGtyTa7ksErF3hyDyAq1aTbo5crK9dAL7zSHh9viKY1BtMls1QOA==} - engines: {node: '>=18'} - - '@tanstack/devtools-event-client@0.4.3': - resolution: {integrity: sha512-OZI6QyULw0FI0wjgmeYzCIfbgPsOEzwJtCpa69XrfLMtNXLGnz3d/dIabk7frg0TmHo+Ah49w5I4KC7Tufwsvw==} - engines: {node: '>=18'} - hasBin: true - - '@tanstack/devtools-ui@0.5.2': - resolution: {integrity: sha512-GtaMk8kaGZ9ZdR8Pu5RAfcse/ZrxzH/xsAIFtHMapLs2VMqSPFfb1NvIDO1MAAfUcub8Ix8XKQEP0uYSPzoFKw==} - engines: {node: '>=18'} - peerDependencies: - solid-js: '>=1.9.7' - - '@tanstack/devtools-vite@0.7.0': - resolution: {integrity: sha512-VXki7K+Xwnpo3IKdNSWGe7YOvtZv33YlulGqaQ+YCpeQhYg8JFuxP50BXibDoRLj5EOX4r21Hs7COdxbRHXkTw==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - - '@tanstack/devtools@0.12.2': - resolution: {integrity: sha512-Xdl8pLzoDUvXaclQ0poY36WAPx0jEHk8vqUFd8FYFUm1BMshtB7RnTgD1HE9jCAXODxqw9I0gXBiUZLK3o3+Bw==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - solid-js: '>=1.9.7' - - '@tanstack/eslint-config@0.4.0': - resolution: {integrity: sha512-V+Cd81W/f65dqKJKpytbwTGx9R+IwxKAHsG/uJ3nSLYEh36hlAr54lRpstUhggQB8nf/cP733cIw8DuD2dzQUg==} - engines: {node: '>=18'} - peerDependencies: - eslint: ^9.0.0 || ^10.0.0 - '@tanstack/history@1.161.6': resolution: {integrity: sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg==} engines: {node: '>=20.19'} @@ -5796,51 +4321,11 @@ packages: '@tanstack/query-core@5.90.10': resolution: {integrity: sha512-EhZVFu9rl7GfRNuJLJ3Y7wtbTnENsvzp+YpcAV7kCYiXni1v8qZh++lpw4ch4rrwC0u/EZRnBHIehzCGzwXDSQ==} - '@tanstack/query-devtools@5.101.0': - resolution: {integrity: sha512-MVqw17k08RQtGGLEL654+dX/btbX9p/8WjkznO//zusLTMaObxi3Q+MoFwGVkC9K3tqjn8qrrNhJevXx4fJTeQ==} - - '@tanstack/react-devtools@0.10.5': - resolution: {integrity: sha512-orVsRJ7oAXFb7oyafQCgx9YuK44jpILh5T/ddYuxAsolNfN5DZBr5/NLrWErD7HCGIzvYzg1TZI4sPxmiKvtvA==} - engines: {node: '>=18'} - peerDependencies: - '@types/react': '>=16.8' - '@types/react-dom': '>=16.8' - react: 19.2.7 - react-dom: 19.2.7 - - '@tanstack/react-query-devtools@5.101.0': - resolution: {integrity: sha512-cpZA0+WqKXwrwMfiWZEGGF6QrIWVQFbhBtxqDF5sQsAfrFf47HIE6fiPbQU3wyAUEN2+7UNqLCQe7oG6m3f93w==} - peerDependencies: - '@tanstack/react-query': ^5.90.2 - react: 19.2.7 - '@tanstack/react-query@5.90.10': resolution: {integrity: sha512-BKLss9Y8PQ9IUjPYQiv3/Zmlx92uxffUOX8ZZNoQlCIZBJPT5M+GOMQj7xislvVQ6l1BstBjcX0XB/aHfFYVNw==} peerDependencies: react: 19.2.7 - '@tanstack/react-router-devtools@1.167.0': - resolution: {integrity: sha512-nGw095EG7IHx0h5NtlEmzf6vcCTaFNPWdTSuDKazajhN0ct/v/TkekJ9J6KYUCeV1a8/2ZmToc58M+0rrOyn7w==} - engines: {node: '>=20.19'} - peerDependencies: - '@tanstack/react-router': ^1.170.0 - '@tanstack/router-core': ^1.170.0 - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@tanstack/router-core': - optional: true - - '@tanstack/react-router-ssr-query@1.167.1': - resolution: {integrity: sha512-W9j5JPnBikyafvuUfykFfHIWod58OAbAAa5leNkXBcoDoocghMmu6w9uZOmUZvAWT7CSvgj5tBUtF7CM2OoHXQ==} - engines: {node: '>=20.19'} - peerDependencies: - '@tanstack/query-core': '>=5.90.0' - '@tanstack/react-query': ^5.90.2 - '@tanstack/react-router': '>=1.127.0' - react: 19.2.7 - react-dom: 19.2.7 - '@tanstack/react-router@1.168.10': resolution: {integrity: sha512-/RmDlOwDkCug609KdPB3U+U1zmrtadJpvsmRg2zEn8TRCKRNri7dYZIjQZbNg8PgUiRL4T6njrZBV1ChzblNaA==} engines: {node: '>=20.19'} @@ -5882,16 +4367,6 @@ packages: engines: {node: '>=20.19'} hasBin: true - '@tanstack/router-devtools-core@1.168.0': - resolution: {integrity: sha512-wQoQhlBK7nlZgqzaqdYXKWNTpdHdsaREdaPhFZVH0/Ador+F+eM3/NF2i3f2LPeS0GgKraZUQXe1Q/1+KHyEYg==} - engines: {node: '>=20.19'} - peerDependencies: - '@tanstack/router-core': ^1.170.0 - csstype: ^3.0.10 - peerDependenciesMeta: - csstype: - optional: true - '@tanstack/router-generator@1.166.24': resolution: {integrity: sha512-vdaGKwuH+r+DPe6R1mjk+TDDmDH6NTG7QqwxHqGEvOH4aGf9sPjhmRKNJZqQr8cPIbfp6u5lXyZ1TeDcSNMVEA==} engines: {node: '>=20.19'} @@ -5918,13 +4393,6 @@ packages: webpack: optional: true - '@tanstack/router-ssr-query-core@1.169.1': - resolution: {integrity: sha512-rngux8s/3mPQzcjLYDLkNU31coYVyCgrVTfpdwqUdY5jIEHqGTXrO73DTkPR1PppwYUeVhmNCgl8TctRcnupjg==} - engines: {node: '>=20.19'} - peerDependencies: - '@tanstack/query-core': '>=5.90.0' - '@tanstack/router-core': '>=1.127.0' - '@tanstack/router-utils@1.161.6': resolution: {integrity: sha512-nRcYw+w2OEgK6VfjirYvGyPLOK+tZQz1jkYcmH5AjMamQ9PycnlxZF2aEZtPpNoUsaceX2bHptn6Ub5hGXqNvw==} engines: {node: '>=20.19'} @@ -5965,25 +4433,6 @@ packages: engines: {node: '>=20.19'} hasBin: true - '@testing-library/dom@10.4.1': - resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} - engines: {node: '>=18'} - - '@testing-library/react@16.3.2': - resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} - engines: {node: '>=18'} - peerDependencies: - '@testing-library/dom': ^10.0.0 - '@types/react': ^18.0.0 || ^19.0.0 - '@types/react-dom': ^18.0.0 || ^19.0.0 - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@tiptap/core@3.20.0': resolution: {integrity: sha512-aC9aROgia/SpJqhsXFiX9TsligL8d+oeoI8W3u00WI45s0VfsqjgeKQLDLF7Tu7hC+7F02teC84SAHuup003VQ==} peerDependencies: @@ -6209,21 +4658,6 @@ packages: '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} - '@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/bun@1.3.2': resolution: {integrity: sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg==} @@ -6240,9 +4674,6 @@ packages: resolution: {integrity: sha512-o7jqJM04gfaYrdCecCVMbZhNdG6T1MHg/oQoRFdERLV+4d+V7FijhiEAbFu0Usww84Yijk9yH58U4Jk4HbtzZw==} deprecated: This is a stub types definition. diff provides its own type definitions, so you do not need this installed. - '@types/esrecurse@4.3.1': - resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} - '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -6258,9 +4689,6 @@ packages: '@types/inquirer@6.5.0': resolution: {integrity: sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==} - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} @@ -6298,9 +4726,6 @@ packages: '@types/node@20.19.25': resolution: {integrity: sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==} - '@types/node@22.19.20': - resolution: {integrity: sha512-6tELRwSDYWW9EdZhbeZmYGZ1/7Djkt+Ah3/ScEYT9cDord7UJzasR/4D3VONg9tQI5CDp+/CZC1AXj2pCFOvpw==} - '@types/node@24.0.3': resolution: {integrity: sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==} @@ -6371,14 +4796,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/eslint-plugin@8.61.0': - resolution: {integrity: sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.61.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.58.0': resolution: {integrity: sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6386,13 +4803,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.61.0': - resolution: {integrity: sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.58.0': resolution: {integrity: sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6432,13 +4842,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.61.0': - resolution: {integrity: sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.58.0': resolution: {integrity: sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6749,18 +5152,9 @@ packages: resolution: {integrity: sha512-yNEQvPcVrK9sIe637+I0jD6leluPxzwJKx/Haw6F4H77CdDsszUn5V3o96LPziXkSNE2B83+Z3mjqGKBK/R6Gg==} engines: {node: '>= 20'} - '@vitejs/plugin-react@5.2.0': - resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==} - engines: {node: ^20.19.0 || >=22.12.0} - peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@vitest/expect@4.1.8': - resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} - '@vitest/mocker@3.2.4': resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: @@ -6772,47 +5166,21 @@ packages: vite: optional: true - '@vitest/mocker@4.1.8': - resolution: {integrity: sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/pretty-format@4.1.8': - resolution: {integrity: sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==} - '@vitest/runner@3.2.4': resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} - '@vitest/runner@4.1.8': - resolution: {integrity: sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==} - '@vitest/snapshot@3.2.4': resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} - '@vitest/snapshot@4.1.8': - resolution: {integrity: sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==} - '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/spy@4.1.8': - resolution: {integrity: sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==} - '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - '@vitest/utils@4.1.8': - resolution: {integrity: sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==} - '@vue/compiler-core@3.5.24': resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==} @@ -6842,10 +5210,6 @@ packages: '@vue/shared@3.5.24': resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==} - accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} - accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -6917,10 +5281,6 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - ansis@4.2.0: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} @@ -6936,9 +5296,6 @@ packages: arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - arg@5.0.2: - resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -6949,9 +5306,6 @@ packages: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} - aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} - aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -6960,9 +5314,6 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} - array-flatten@1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - array-includes@3.1.9: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} @@ -7068,10 +5419,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - basic-auth@2.0.1: - resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} - engines: {node: '>= 0.8'} - basic-ftp@5.0.5: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} @@ -7157,10 +5504,6 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - body-parser@1.20.5: - resolution: {integrity: sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.2.2: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} @@ -7199,9 +5542,6 @@ packages: resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==} engines: {node: '>=16.20.1'} - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -7278,10 +5618,6 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} - chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} - engines: {node: '>=18'} - chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -7443,14 +5779,6 @@ packages: commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} - compressible@2.0.18: - resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} - engines: {node: '>= 0.6'} - - compression@1.8.1: - resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} - engines: {node: '>= 0.8.0'} - compute-scroll-into-view@3.1.1: resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} @@ -7470,10 +5798,6 @@ packages: constant-case@2.0.0: resolution: {integrity: sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==} - content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} - content-disposition@1.0.1: resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} engines: {node: '>=18'} @@ -7488,9 +5812,6 @@ packages: cookie-es@2.0.1: resolution: {integrity: sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA==} - cookie-signature@1.0.7: - resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} - cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -7625,40 +5946,6 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} - dayjs@1.11.21: - resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} - - db0@0.3.4: - resolution: {integrity: sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==} - peerDependencies: - '@electric-sql/pglite': '*' - '@libsql/client': '*' - better-sqlite3: '*' - drizzle-orm: '*' - mysql2: '*' - sqlite3: '*' - peerDependenciesMeta: - '@electric-sql/pglite': - optional: true - '@libsql/client': - optional: true - better-sqlite3: - optional: true - drizzle-orm: - optional: true - mysql2: - optional: true - sqlite3: - optional: true - - debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -7769,10 +6056,6 @@ packages: destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -7803,9 +6086,6 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} - dom-accessibility-api@0.5.16: - resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -7890,10 +6170,6 @@ packages: resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} - enhanced-resolve@5.23.0: - resolution: {integrity: sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==} - engines: {node: '>=10.13.0'} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -7914,21 +6190,6 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} - env-runner@0.1.12: - resolution: {integrity: sha512-pHBoUIdcYeDUDzLDcsTydFxCVn/rtyQqwcPktpD1VElFEZ58udPfk3NrVxF2MG/sxj8yxGbwbTI7Pqh73u/isA==} - hasBin: true - peerDependencies: - '@netlify/runtime': ^4.1.23 - '@vercel/queue': ^0.2.0 - miniflare: ^4.20260515.0 - peerDependenciesMeta: - '@netlify/runtime': - optional: true - '@vercel/queue': - optional: true - miniflare: - optional: true - error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -7951,9 +6212,6 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - es-module-lexer@2.1.0: - resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} - es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -8015,12 +6273,6 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-compat-utils@0.5.1: - resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} - engines: {node: '>=12'} - peerDependencies: - eslint: '>=6.0.0' - eslint-config-next@15.3.4: resolution: {integrity: sha512-WqeumCq57QcTP2lYlV6BRUySfGiBYEXlQ1L0mQ+u4N4X4ZhUVSSQ52WtjqHv60pJ6dD7jn+YZc0d1/ZSsxccvg==} peerDependencies: @@ -8030,15 +6282,6 @@ packages: typescript: optional: true - eslint-config-next@16.1.7: - resolution: {integrity: sha512-FTq1i/QDltzq+zf9aB/cKWAiZ77baG0V7h8dRQh3thVx7I4dwr6ZXQrWKAaTB7x5VwVXlzoUTyMLIVQPLj2gJg==} - peerDependencies: - eslint: '>=9.0.0' - typescript: '>=3.3.1' - peerDependenciesMeta: - typescript: - optional: true - eslint-import-context@0.1.9: resolution: {integrity: sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -8085,12 +6328,6 @@ packages: eslint-import-resolver-webpack: optional: true - eslint-plugin-es-x@7.8.0: - resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '>=8' - eslint-plugin-import-x@4.16.2: resolution: {integrity: sha512-rM9K8UBHcWKpzQzStn1YRN2T5NvdeIfSVoKu/lKF41znQXHAUcBbYXe5wd6GNjZjTrP7viQ49n1D83x/2gYgIw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -8120,24 +6357,12 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - eslint-plugin-n@17.24.0: - resolution: {integrity: sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: '>=8.23.0' - eslint-plugin-react-hooks@5.2.0: resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - eslint-plugin-react-hooks@7.1.1: - resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} - engines: {node: '>=18'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 - eslint-plugin-react@7.37.5: resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} engines: {node: '>=4'} @@ -8148,22 +6373,10 @@ packages: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-scope@9.1.2: - resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint-visitor-keys@5.0.1: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -8174,24 +6387,6 @@ packages: deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true - eslint@9.39.4: - resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - - espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - espree@11.2.0: - resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -8264,28 +6459,16 @@ packages: resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} engines: {node: ^18.19.0 || >=20.5.0} - exit-hook@2.2.1: - resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} - engines: {node: '>=6'} - expect-type@1.2.2: resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} - engines: {node: '>=12.0.0'} - express-rate-limit@8.3.2: resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' - express@4.22.2: - resolution: {integrity: sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==} - engines: {node: '>= 0.10.0'} - express@5.2.1: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} @@ -8369,18 +6552,10 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@1.3.2: - resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} - engines: {node: '>= 0.8'} - finalhandler@2.1.1: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} @@ -8396,10 +6571,6 @@ packages: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} @@ -8445,10 +6616,6 @@ packages: react-dom: optional: true - fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - fresh@2.0.0: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} @@ -8612,10 +6779,6 @@ packages: get-port-please@3.2.0: resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} - get-port@5.1.1: - resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} - engines: {node: '>=8'} - get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -8665,22 +6828,6 @@ packages: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - - globals@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} - engines: {node: '>=18'} - - globals@16.4.0: - resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} - engines: {node: '>=18'} - - globals@17.6.0: - resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} - engines: {node: '>=18'} - globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -8689,14 +6836,6 @@ packages: resolution: {integrity: sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==} engines: {node: '>=8'} - globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - - goober@2.1.19: - resolution: {integrity: sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg==} - peerDependencies: - csstype: ^3.0.10 - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -8731,16 +6870,6 @@ packages: crossws: optional: true - h3@2.0.1-rc.22: - resolution: {integrity: sha512-Esv0DMIuPkCTSWCA0vO73vcTqwzH1wjSrAO1TXNu/K3up1sZHa9EKMapbmxCDYBeymC3fVTk4qxp7ogQWQ+KgA==} - engines: {node: '>=20.11.1'} - hasBin: true - peerDependencies: - crossws: ^0.4.1 - peerDependenciesMeta: - crossws: - optional: true - handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -8837,12 +6966,6 @@ packages: helper-js@3.1.6: resolution: {integrity: sha512-lhncHDAxS2PTA44aG1AofxT51v0IXEVOmaUCC6HwuGaGqE1yEkjhPH74Vb/Aw4xt8Kt5bMvStCr6FekANp+PGg==} - hermes-estree@0.25.1: - resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} - - hermes-parser@0.25.1: - resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -8860,9 +6983,6 @@ packages: hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} - hookable@6.1.1: - resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} - html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -8891,9 +7011,6 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} - httpxy@0.5.3: - resolution: {integrity: sha512-SMS9V6Sn7VWaS11lYhoAr0ceoaiolTWf4jYdJn0NJhCdKMu9R2H9Fh0LBDWBHQF6HRLI1PmaePYsjanSpE5PEw==} - human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -9263,11 +7380,6 @@ packages: canvas: optional: true - jsesc@3.0.2: - resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} - engines: {node: '>=6'} - hasBin: true - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -9350,9 +7462,6 @@ packages: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} - launch-editor@2.14.1: - resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -9537,20 +7646,11 @@ packages: peerDependencies: react: 19.2.7 - lucide-react@0.545.0: - resolution: {integrity: sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw==} - peerDependencies: - react: 19.2.7 - lucide-react@1.7.0: resolution: {integrity: sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==} peerDependencies: react: 19.2.7 - lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} - hasBin: true - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -9640,10 +7740,6 @@ packages: mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - media-typer@1.1.0: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} @@ -9651,9 +7747,6 @@ packages: memory-pager@1.5.0: resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - merge-descriptors@1.0.3: - resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -9665,10 +7758,6 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -9781,27 +7870,14 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - mime-db@1.54.0: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - mime-types@3.0.2: resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} engines: {node: '>=18'} - mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -9882,19 +7958,12 @@ packages: socks: optional: true - morgan@1.11.0: - resolution: {integrity: sha512-zSkVu3t18r39pw4ixfBKvfZi3y2UOqr7d4WYwcj3m8nXpEQK4rPO6GLzs/CExoRgmX3y9EjmmcXqv6jq0SK46g==} - engines: {node: '>= 0.8.0'} - motion-dom@12.23.23: resolution: {integrity: sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==} motion-utils@12.23.6: resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} - ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -9945,14 +8014,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - - negotiator@0.6.4: - resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} - engines: {node: '>= 0.6'} - negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -10012,40 +8073,6 @@ packages: sass: optional: true - nf3@0.3.17: - resolution: {integrity: sha512-N9zEWySuJFw+gR0lhS5863YsvNeudOdqRyFvNb+jMXbeTJOdrjDqkCpDginIZfUm0LzT1t1nCRiDeqQm/8kirQ==} - - nitro@3.0.260603-beta: - resolution: {integrity: sha512-ffaSHK00a7YDlDizoEHwcxPwpQpdBRRA8k42ymTsRnfl3ipGeKgv4gnPr6DgmCNTo4tYVPK3bHBEv1gNhWpo/A==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@vercel/queue': ^0.2.0 - dotenv: '*' - giget: '*' - jiti: ^2.7.0 - rollup: ^4.60.4 - vite: ^7 || ^8 - xml2js: ^0.6.2 - zephyr-agent: ^0.2.0 - peerDependenciesMeta: - '@vercel/queue': - optional: true - dotenv: - optional: true - giget: - optional: true - jiti: - optional: true - rollup: - optional: true - vite: - optional: true - xml2js: - optional: true - zephyr-agent: - optional: true - no-case@2.3.2: resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} @@ -10161,16 +8188,6 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} - obug@2.1.2: - resolution: {integrity: sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==} - engines: {node: '>=12.20.0'} - - ocache@0.1.5: - resolution: {integrity: sha512-kNNnkkVQup/QDvmTz8Q84wc2ntiyoVHDxa6eHWKt5qdGAmFRBIxy83rxgCYEjW0x06UJ9E3P6VgM2yY4rOBH4w==} - - ofetch@2.0.0-alpha.3: - resolution: {integrity: sha512-zpYTCs2byOuft65vI3z43Dd6iSdFbOZZLb9/d21aCpx2rGastVU9dOCv0lu4ykc1Ur1anAYjDi3SUvR0vq50JA==} - ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} @@ -10178,10 +8195,6 @@ packages: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} - on-headers@1.1.0: - resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} - engines: {node: '>= 0.8'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -10237,10 +8250,6 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} - oxc-parser@0.120.0: - resolution: {integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==} - engines: {node: ^20.19.0 || >=22.12.0} - oxc-resolver@11.19.1: resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==} @@ -10260,10 +8269,6 @@ packages: resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} engines: {node: '>=8'} - p-map@7.0.4: - resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} - engines: {node: '>=18'} - pac-proxy-agent@7.2.0: resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} engines: {node: '>= 14'} @@ -10341,9 +8346,6 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-to-regexp@0.1.13: - resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==} - path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -10354,9 +8356,6 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -10646,116 +8645,6 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-plugin-tailwindcss@0.7.4: - resolution: {integrity: sha512-UKii4RjY05SNt/WQi6/NcOn/LsT0/ILLXsxygjbRg5/YZelsSu5jTqorYHPDGq4nZy5q5hpCu+XdGZ1xaJEQgw==} - engines: {node: '>=20.19'} - peerDependencies: - '@ianvs/prettier-plugin-sort-imports': '*' - '@prettier/plugin-hermes': '*' - '@prettier/plugin-oxc': '*' - '@prettier/plugin-pug': '*' - '@shopify/prettier-plugin-liquid': '*' - '@trivago/prettier-plugin-sort-imports': '*' - '@zackad/prettier-plugin-twig': '*' - prettier: ^3.0 - prettier-plugin-astro: '*' - prettier-plugin-css-order: '*' - prettier-plugin-jsdoc: '*' - prettier-plugin-marko: '*' - prettier-plugin-multiline-arrays: '*' - prettier-plugin-organize-attributes: '*' - prettier-plugin-organize-imports: '*' - prettier-plugin-sort-imports: '*' - prettier-plugin-svelte: '*' - peerDependenciesMeta: - '@ianvs/prettier-plugin-sort-imports': - optional: true - '@prettier/plugin-hermes': - optional: true - '@prettier/plugin-oxc': - optional: true - '@prettier/plugin-pug': - optional: true - '@shopify/prettier-plugin-liquid': - optional: true - '@trivago/prettier-plugin-sort-imports': - optional: true - '@zackad/prettier-plugin-twig': - optional: true - prettier-plugin-astro: - optional: true - prettier-plugin-css-order: - optional: true - prettier-plugin-jsdoc: - optional: true - prettier-plugin-marko: - optional: true - prettier-plugin-multiline-arrays: - optional: true - prettier-plugin-organize-attributes: - optional: true - prettier-plugin-organize-imports: - optional: true - prettier-plugin-sort-imports: - optional: true - prettier-plugin-svelte: - optional: true - - prettier-plugin-tailwindcss@0.8.0: - resolution: {integrity: sha512-V8ITGH87yuBDF6JpEZTOVlUz/saAwqb8f3HRgUj8Lh+tGCcrmorhsLpYqzygwFwK0PE2Ib6Mv3M7T/uE2tZV1g==} - engines: {node: '>=20.19'} - peerDependencies: - '@ianvs/prettier-plugin-sort-imports': '*' - '@prettier/plugin-hermes': '*' - '@prettier/plugin-oxc': '*' - '@prettier/plugin-pug': '*' - '@shopify/prettier-plugin-liquid': '*' - '@trivago/prettier-plugin-sort-imports': '*' - '@zackad/prettier-plugin-twig': '*' - prettier: ^3.0 - prettier-plugin-astro: '*' - prettier-plugin-css-order: '*' - prettier-plugin-jsdoc: '*' - prettier-plugin-marko: '*' - prettier-plugin-multiline-arrays: '*' - prettier-plugin-organize-attributes: '*' - prettier-plugin-organize-imports: '*' - prettier-plugin-sort-imports: '*' - prettier-plugin-svelte: '*' - peerDependenciesMeta: - '@ianvs/prettier-plugin-sort-imports': - optional: true - '@prettier/plugin-hermes': - optional: true - '@prettier/plugin-oxc': - optional: true - '@prettier/plugin-pug': - optional: true - '@shopify/prettier-plugin-liquid': - optional: true - '@trivago/prettier-plugin-sort-imports': - optional: true - '@zackad/prettier-plugin-twig': - optional: true - prettier-plugin-astro: - optional: true - prettier-plugin-css-order: - optional: true - prettier-plugin-jsdoc: - optional: true - prettier-plugin-marko: - optional: true - prettier-plugin-multiline-arrays: - optional: true - prettier-plugin-organize-attributes: - optional: true - prettier-plugin-organize-imports: - optional: true - prettier-plugin-sort-imports: - optional: true - prettier-plugin-svelte: - optional: true - prettier@3.8.4: resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==} engines: {node: '>=14'} @@ -10765,10 +8654,6 @@ packages: resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} engines: {node: '>=20'} - pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - pretty-hrtime@1.0.3: resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} engines: {node: '>= 0.8'} @@ -10914,10 +8799,6 @@ packages: resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} - qs@6.15.2: - resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} - engines: {node: '>=0.6'} - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -10934,27 +8815,10 @@ packages: '@types/react-dom': optional: true - radix-ui@1.5.0: - resolution: {integrity: sha512-Nzh2HNpClgB31FBHRqt2xG8XNUfVfQRpf34hACC5PNrXTd5JdXdqOXwLs3BL+D8CNYiNQiJiT8QGr5Q4vq+00w==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 19.2.7 - react-dom: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - raw-body@2.5.3: - resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} - engines: {node: '>= 0.8'} - raw-body@3.0.2: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} @@ -11002,9 +8866,6 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - react-markdown@9.1.0: resolution: {integrity: sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==} peerDependencies: @@ -11017,14 +8878,6 @@ packages: react: 19.2.7 react-dom: 19.2.7 - react-refresh@0.14.2: - resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} - engines: {node: '>=0.10.0'} - - react-refresh@0.18.0: - resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} - engines: {node: '>=0.10.0'} - react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -11045,16 +8898,6 @@ packages: '@types/react': optional: true - react-remove-scroll@2.7.2: - resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: 19.2.7 - peerDependenciesMeta: - '@types/react': - optional: true - react-resizable-panels@2.1.9: resolution: {integrity: sha512-z77+X08YDIrgAes4jl8xhnUu1LNIRp4+E7cv4xHmLOxxUPO/ML7PSrE813b90vj7xvQ1lcf7g2uA9GeMZonjhQ==} peerDependencies: @@ -11333,9 +9176,6 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -11385,10 +9225,6 @@ packages: engines: {node: '>=10'} hasBin: true - send@0.19.2: - resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} - engines: {node: '>= 0.8.0'} - send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -11409,10 +9245,6 @@ packages: resolution: {integrity: sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw==} engines: {node: '>=10'} - serve-static@1.16.3: - resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} - engines: {node: '>= 0.8.0'} - serve-static@2.2.1: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} @@ -11442,10 +9274,6 @@ packages: resolution: {integrity: sha512-qNQcCavkbYsgBj+X09tF2bTcwRd8abR880bsFkDU2kMqceMCLAm5c+cLg7kWDhfh1H9g08knpQ5ZEf6y/co16g==} hasBin: true - shadcn@4.11.0: - resolution: {integrity: sha512-UV0cchFea9hO7poV1CuEP0wvmYjpAqcxCKdy23bndl2Du2ARtDs8A4xdzfhUjDBeOW1nNpJ6lXmsEpsply2SfQ==} - hasBin: true - sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -11458,10 +9286,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.4: - resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==} - engines: {node: '>= 0.4'} - shiki@3.15.0: resolution: {integrity: sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw==} @@ -11538,9 +9362,6 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -11581,9 +9402,6 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - std-env@4.1.0: - resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} - stdin-discarder@0.2.2: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} @@ -11746,17 +9564,10 @@ packages: tailwindcss@4.2.2: resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} - tailwindcss@4.3.0: - resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==} - tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} - tapable@2.3.3: - resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} - engines: {node: '>=6'} - text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -11805,10 +9616,6 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} - engines: {node: '>=14.0.0'} - tinyspy@4.0.4: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} @@ -11859,11 +9666,6 @@ packages: peerDependencies: typescript: '>=4.8.4' - ts-declaration-location@1.0.7: - resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} - peerDependencies: - typescript: '>=4.0.0' - ts-morph@26.0.0: resolution: {integrity: sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==} @@ -11884,16 +9686,6 @@ packages: '@swc/wasm': optional: true - tsconfck@3.1.6: - resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} - engines: {node: ^18 || >=20} - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -11970,10 +9762,6 @@ packages: resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} engines: {node: '>=20'} - type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -11994,23 +9782,11 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.61.0: - resolution: {integrity: sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' - typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true - typescript@6.0.3: - resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} - engines: {node: '>=14.17'} - hasBin: true - uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} @@ -12062,9 +9838,6 @@ packages: resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==} engines: {node: '>=20.18.1'} - unenv@2.0.0-rc.24: - resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} - unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} @@ -12114,80 +9887,6 @@ packages: unrs-resolver@1.12.2: resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} - unstorage@2.0.0-alpha.7: - resolution: {integrity: sha512-ELPztchk2zgFJnakyodVY3vJWGW9jy//keJ32IOJVGUMyaPydwcA1FtVvWqT0TNRch9H+cMNEGllfVFfScImog==} - peerDependencies: - '@azure/app-configuration': ^1.11.0 - '@azure/cosmos': ^4.9.1 - '@azure/data-tables': ^13.3.2 - '@azure/identity': ^4.13.0 - '@azure/keyvault-secrets': ^4.10.0 - '@azure/storage-blob': ^12.31.0 - '@capacitor/preferences': ^6 || ^7 || ^8 - '@deno/kv': '>=0.13.0' - '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 - '@planetscale/database': ^1.19.0 - '@upstash/redis': ^1.36.2 - '@vercel/blob': '>=0.27.3' - '@vercel/functions': ^2.2.12 || ^3.0.0 - '@vercel/kv': ^1.0.1 - aws4fetch: ^1.0.20 - chokidar: ^4 || ^5 - db0: '>=0.3.4' - idb-keyval: ^6.2.2 - ioredis: ^5.9.3 - lru-cache: ^11.2.6 - mongodb: ^6 || ^7 - ofetch: '*' - uploadthing: ^7.7.4 - peerDependenciesMeta: - '@azure/app-configuration': - optional: true - '@azure/cosmos': - optional: true - '@azure/data-tables': - optional: true - '@azure/identity': - optional: true - '@azure/keyvault-secrets': - optional: true - '@azure/storage-blob': - optional: true - '@capacitor/preferences': - optional: true - '@deno/kv': - optional: true - '@netlify/blobs': - optional: true - '@planetscale/database': - optional: true - '@upstash/redis': - optional: true - '@vercel/blob': - optional: true - '@vercel/functions': - optional: true - '@vercel/kv': - optional: true - aws4fetch: - optional: true - chokidar: - optional: true - db0: - optional: true - idb-keyval: - optional: true - ioredis: - optional: true - lru-cache: - optional: true - mongodb: - optional: true - ofetch: - optional: true - uploadthing: - optional: true - until-async@3.0.2: resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} @@ -12252,10 +9951,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -12299,14 +9994,6 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite-tsconfig-paths@5.1.4: - resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} - peerDependencies: - vite: '*' - peerDependenciesMeta: - vite: - optional: true - vite@7.3.1: resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -12374,48 +10061,7 @@ packages: optional: true '@types/node': optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - vitest@4.1.8: - resolution: {integrity: sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.8 - '@vitest/browser-preview': 4.1.8 - '@vitest/browser-webdriverio': 4.1.8 - '@vitest/coverage-istanbul': 4.1.8 - '@vitest/coverage-v8': 4.1.8 - '@vitest/ui': 4.1.8 - happy-dom: '*' - jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/coverage-istanbul': - optional: true - '@vitest/coverage-v8': + '@vitest/browser': optional: true '@vitest/ui': optional: true @@ -12424,12 +10070,6 @@ packages: jsdom: optional: true - vue-eslint-parser@10.4.1: - resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - vue@3.5.24: resolution: {integrity: sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==} peerDependencies: @@ -12540,18 +10180,6 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.21.0: - resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - wsl-utils@0.3.1: resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} engines: {node: '>=20'} @@ -12611,12 +10239,6 @@ packages: peerDependencies: zod: 4.4.3 - zod-validation-error@4.0.2: - resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - zod: 4.4.3 - zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -12648,7 +10270,8 @@ packages: snapshots: - '@acemir/cssom@0.9.31': {} + '@acemir/cssom@0.9.31': + optional: true '@ai-sdk/gateway@2.0.10(zod@4.4.3)': dependencies: @@ -12657,12 +10280,6 @@ snapshots: '@vercel/oidc': 3.0.3 zod: 4.4.3 - '@ai-sdk/openai@2.0.106(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 2.0.3 - '@ai-sdk/provider-utils': 3.0.25(zod@4.4.3) - zod: 4.4.3 - '@ai-sdk/provider-utils@3.0.17(zod@4.4.3)': dependencies: '@ai-sdk/provider': 2.0.0 @@ -12670,21 +10287,10 @@ snapshots: eventsource-parser: 3.0.6 zod: 4.4.3 - '@ai-sdk/provider-utils@3.0.25(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 2.0.3 - '@standard-schema/spec': 1.1.0 - eventsource-parser: 3.0.6 - zod: 4.4.3 - '@ai-sdk/provider@2.0.0': dependencies: json-schema: 0.4.0 - '@ai-sdk/provider@2.0.3': - dependencies: - json-schema: 0.4.0 - '@ai-sdk/react@2.0.94(react@19.2.7)(zod@4.4.3)': dependencies: '@ai-sdk/provider-utils': 3.0.17(zod@4.4.3) @@ -12704,6 +10310,7 @@ snapshots: '@csstools/css-color-parser': 4.1.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 + optional: true '@asamuzakjp/dom-selector@6.8.1': dependencies: @@ -12712,10 +10319,13 @@ snapshots: css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 lru-cache: 11.5.1 + optional: true - '@asamuzakjp/generational-cache@1.0.1': {} + '@asamuzakjp/generational-cache@1.0.1': + optional: true - '@asamuzakjp/nwsapi@2.3.9': {} + '@asamuzakjp/nwsapi@2.3.9': + optional: true '@aws-crypto/crc32@5.2.0': dependencies: @@ -13184,6 +10794,7 @@ snapshots: '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 + optional: true '@babel/code-frame@7.29.0': dependencies: @@ -13200,7 +10811,8 @@ snapshots: '@babel/compat-data@7.29.0': {} - '@babel/compat-data@7.29.7': {} + '@babel/compat-data@7.29.7': + optional: true '@babel/core@7.29.0': dependencies: @@ -13241,6 +10853,7 @@ snapshots: semver: 6.3.1 transitivePeerDependencies: - supports-color + optional: true '@babel/generator@7.29.7': dependencies: @@ -13269,6 +10882,7 @@ snapshots: browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 + optional: true '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.29.0)': dependencies: @@ -13283,19 +10897,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.29.7) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.29.7 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/helper-globals@7.29.7': {} '@babel/helper-member-expression-to-functions@7.28.5': @@ -13345,6 +10946,7 @@ snapshots: '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color + optional: true '@babel/helper-optimise-call-expression@7.27.1': dependencies: @@ -13363,15 +10965,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.27.1(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.29.7 - transitivePeerDependencies: - - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: '@babel/traverse': 7.29.7 @@ -13388,7 +10981,8 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helper-validator-option@7.29.7': {} + '@babel/helper-validator-option@7.29.7': + optional: true '@babel/helpers@7.29.2': dependencies: @@ -13399,6 +10993,7 @@ snapshots: dependencies: '@babel/template': 7.29.7 '@babel/types': 7.29.7 + optional: true '@babel/parser@7.29.2': dependencies: @@ -13417,21 +11012,18 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + optional: true '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + optional: true '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.29.0)': dependencies: @@ -13441,24 +11033,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) - '@babel/helper-plugin-utils': 7.29.7 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -13470,17 +11044,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.29.7) - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.29.7) - transitivePeerDependencies: - - supports-color - '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -13492,17 +11055,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.28.5(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.29.7) - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.7) - transitivePeerDependencies: - - supports-color - '@babel/runtime-corejs3@7.28.4': dependencies: core-js-pure: 3.47.0 @@ -13605,280 +11157,72 @@ snapshots: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) '@better-auth/utils': 0.4.1 optionalDependencies: - '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3) - prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3) - - '@better-auth/prisma-adapter@1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(typescript@6.0.3))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))': - dependencies: - '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - '@better-auth/utils': 0.4.1 - optionalDependencies: - '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(typescript@6.0.3) - prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) - - '@better-auth/telemetry@1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)': - dependencies: - '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - '@better-auth/utils': 0.4.1 - '@better-fetch/fetch': 1.2.2 - - '@better-auth/utils@0.4.0': - dependencies: - '@noble/hashes': 2.0.1 - - '@better-auth/utils@0.4.1': - dependencies: - '@noble/hashes': 2.0.1 - - '@better-fetch/fetch@1.1.21': {} - - '@better-fetch/fetch@1.2.2': {} - - '@biomejs/biome@2.2.4': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.2.4 - '@biomejs/cli-darwin-x64': 2.2.4 - '@biomejs/cli-linux-arm64': 2.2.4 - '@biomejs/cli-linux-arm64-musl': 2.2.4 - '@biomejs/cli-linux-x64': 2.2.4 - '@biomejs/cli-linux-x64-musl': 2.2.4 - '@biomejs/cli-win32-arm64': 2.2.4 - '@biomejs/cli-win32-x64': 2.2.4 - - '@biomejs/cli-darwin-arm64@2.2.4': - optional: true - - '@biomejs/cli-darwin-x64@2.2.4': - optional: true - - '@biomejs/cli-linux-arm64-musl@2.2.4': - optional: true - - '@biomejs/cli-linux-arm64@2.2.4': - optional: true - - '@biomejs/cli-linux-x64-musl@2.2.4': - optional: true - - '@biomejs/cli-linux-x64@2.2.4': - optional: true - - '@biomejs/cli-win32-arm64@2.2.4': - optional: true - - '@biomejs/cli-win32-x64@2.2.4': - optional: true - - '@bramus/specificity@2.4.2': - dependencies: - css-tree: 3.2.1 - - '@btst/adapter-memory@2.2.2(2b2c34516092622e3b8ea86d6d757e06)': - dependencies: - '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - '@btst/db': 2.2.2(1be652907e98e3489967319bbfd782db) - better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@4.1.8(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)))(vue@3.5.24(typescript@5.9.3)) - transitivePeerDependencies: - - '@better-auth/utils' - - '@better-fetch/fetch' - - '@cloudflare/workers-types' - - '@lynx-js/react' - - '@opentelemetry/api' - - '@prisma/client' - - '@sveltejs/kit' - - '@tanstack/react-start' - - '@tanstack/solid-start' - - better-call - - better-sqlite3 - - drizzle-kit - - drizzle-orm - - jose - - kysely - - mongodb - - mysql2 - - nanostores - - next - - pg - - prisma - - react - - react-dom - - solid-js - - svelte - - vitest - - vue - - '@btst/adapter-memory@2.2.2(61db3a5de70a4f07ceea932402bbed8f)': - dependencies: - '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - '@btst/db': 2.2.2(154c028984787222a7ef0cf1c630f192) - better-auth: 1.6.16(1ab4f2557a8135537aff00ad0ec4ded1) - transitivePeerDependencies: - - '@better-auth/utils' - - '@better-fetch/fetch' - - '@cloudflare/workers-types' - - '@lynx-js/react' - - '@opentelemetry/api' - - '@prisma/client' - - '@sveltejs/kit' - - '@tanstack/react-start' - - '@tanstack/solid-start' - - better-call - - better-sqlite3 - - drizzle-kit - - drizzle-orm - - jose - - kysely - - mongodb - - mysql2 - - nanostores - - next - - pg - - prisma - - react - - react-dom - - solid-js - - svelte - - vitest - - vue - - '@btst/adapter-memory@2.2.2(67378101110aa5c0ec2041bf2e8296f1)': - dependencies: - '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - '@btst/db': 2.2.2(6b742495bfb70b190049e28cee40c6a4) - better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) - transitivePeerDependencies: - - '@better-auth/utils' - - '@better-fetch/fetch' - - '@cloudflare/workers-types' - - '@lynx-js/react' - - '@opentelemetry/api' - - '@prisma/client' - - '@sveltejs/kit' - - '@tanstack/react-start' - - '@tanstack/solid-start' - - better-call - - better-sqlite3 - - drizzle-kit - - drizzle-orm - - jose - - kysely - - mongodb - - mysql2 - - nanostores - - next - - pg - - prisma - - react - - react-dom - - solid-js - - svelte - - vitest - - vue + '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3) + prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3) - '@btst/adapter-memory@2.2.2(7282e51985b70ab8a4219751233dfac7)': + '@better-auth/telemetry@1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)': dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - '@btst/db': 2.2.2(74b3f61cab32efd191f5657fe8b1fae0) - better-auth: 1.6.16(21c5674fc5bfa4beefd02b4684417e5e) - transitivePeerDependencies: - - '@better-auth/utils' - - '@better-fetch/fetch' - - '@cloudflare/workers-types' - - '@lynx-js/react' - - '@opentelemetry/api' - - '@prisma/client' - - '@sveltejs/kit' - - '@tanstack/react-start' - - '@tanstack/solid-start' - - better-call - - better-sqlite3 - - drizzle-kit - - drizzle-orm - - jose - - kysely - - mongodb - - mysql2 - - nanostores - - next - - pg - - prisma - - react - - react-dom - - solid-js - - svelte - - vitest - - vue + '@better-auth/utils': 0.4.1 + '@better-fetch/fetch': 1.2.2 - '@btst/db@2.2.2(154c028984787222a7ef0cf1c630f192)': + '@better-auth/utils@0.4.0': dependencies: - '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - better-auth: 1.6.16(1ab4f2557a8135537aff00ad0ec4ded1) - transitivePeerDependencies: - - '@better-auth/utils' - - '@better-fetch/fetch' - - '@cloudflare/workers-types' - - '@lynx-js/react' - - '@opentelemetry/api' - - '@prisma/client' - - '@sveltejs/kit' - - '@tanstack/react-start' - - '@tanstack/solid-start' - - better-call - - better-sqlite3 - - drizzle-kit - - drizzle-orm - - jose - - kysely - - mongodb - - mysql2 - - nanostores - - next - - pg - - prisma - - react - - react-dom - - solid-js - - svelte - - vitest - - vue + '@noble/hashes': 2.0.1 - '@btst/db@2.2.2(1be652907e98e3489967319bbfd782db)': + '@better-auth/utils@0.4.1': dependencies: - '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@4.1.8(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)))(vue@3.5.24(typescript@5.9.3)) - transitivePeerDependencies: - - '@better-auth/utils' - - '@better-fetch/fetch' - - '@cloudflare/workers-types' - - '@lynx-js/react' - - '@opentelemetry/api' - - '@prisma/client' - - '@sveltejs/kit' - - '@tanstack/react-start' - - '@tanstack/solid-start' - - better-call - - better-sqlite3 - - drizzle-kit - - drizzle-orm - - jose - - kysely - - mongodb - - mysql2 - - nanostores - - next - - pg - - prisma - - react - - react-dom - - solid-js - - svelte - - vitest - - vue + '@noble/hashes': 2.0.1 + + '@better-fetch/fetch@1.1.21': {} + + '@better-fetch/fetch@1.2.2': {} + + '@biomejs/biome@2.2.4': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.2.4 + '@biomejs/cli-darwin-x64': 2.2.4 + '@biomejs/cli-linux-arm64': 2.2.4 + '@biomejs/cli-linux-arm64-musl': 2.2.4 + '@biomejs/cli-linux-x64': 2.2.4 + '@biomejs/cli-linux-x64-musl': 2.2.4 + '@biomejs/cli-win32-arm64': 2.2.4 + '@biomejs/cli-win32-x64': 2.2.4 + + '@biomejs/cli-darwin-arm64@2.2.4': + optional: true + + '@biomejs/cli-darwin-x64@2.2.4': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.2.4': + optional: true + + '@biomejs/cli-linux-arm64@2.2.4': + optional: true + + '@biomejs/cli-linux-x64-musl@2.2.4': + optional: true + + '@biomejs/cli-linux-x64@2.2.4': + optional: true - '@btst/db@2.2.2(6b742495bfb70b190049e28cee40c6a4)': + '@biomejs/cli-win32-arm64@2.2.4': + optional: true + + '@biomejs/cli-win32-x64@2.2.4': + optional: true + + '@bramus/specificity@2.4.2': + dependencies: + css-tree: 3.2.1 + optional: true + + '@btst/adapter-memory@2.2.2(3f1048c33dcc3a34f5463c3b01c66883)': dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) + '@btst/db': 2.2.2(7010515aebf7f15e3f6fd94d16578921) + better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) transitivePeerDependencies: - '@better-auth/utils' - '@better-fetch/fetch' @@ -13908,10 +11252,10 @@ snapshots: - vitest - vue - '@btst/db@2.2.2(74b3f61cab32efd191f5657fe8b1fae0)': + '@btst/db@2.2.2(7010515aebf7f15e3f6fd94d16578921)': dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - better-auth: 1.6.16(21c5674fc5bfa4beefd02b4684417e5e) + better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) transitivePeerDependencies: - '@better-auth/utils' - '@better-fetch/fetch' @@ -13941,10 +11285,11 @@ snapshots: - vitest - vue - '@btst/yar@1.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)': + '@btst/yar@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react@19.2.7)': dependencies: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + react: 19.2.7 rou3: 0.7.12 '@chevrotain/cst-dts-gen@10.5.0': @@ -14236,12 +11581,14 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@csstools/color-helpers@6.0.2': {} + '@csstools/color-helpers@6.0.2': + optional: true '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 + optional: true '@csstools/css-color-parser@4.1.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: @@ -14249,16 +11596,20 @@ snapshots: '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 + optional: true '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-tokenizer': 4.0.0 + optional: true '@csstools/css-syntax-patches-for-csstree@1.1.5(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 + optional: true - '@csstools/css-tokenizer@4.0.0': {} + '@csstools/css-tokenizer@4.0.0': + optional: true '@date-fns/tz@1.4.1': {} @@ -14589,29 +11940,8 @@ snapshots: eslint: 8.57.1 eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))': - dependencies: - eslint: 9.39.4(jiti@2.6.1) - eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.2': - dependencies: - '@eslint/object-schema': 2.1.7 - debug: 4.4.3 - minimatch: 3.1.5 - transitivePeerDependencies: - - supports-color - - '@eslint/config-helpers@0.4.2': - dependencies: - '@eslint/core': 0.17.0 - - '@eslint/core@0.17.0': - dependencies: - '@types/json-schema': 7.0.15 - '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.14.0 @@ -14626,38 +11956,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/eslintrc@3.3.5': - dependencies: - ajv: 6.14.0 - debug: 4.4.3 - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.2.0 - minimatch: 3.1.5 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@10.0.1(eslint@9.39.4(jiti@2.6.1))': - optionalDependencies: - eslint: 9.39.4(jiti@2.6.1) - '@eslint/js@8.57.1': {} - '@eslint/js@9.39.4': {} - - '@eslint/object-schema@2.1.7': {} - - '@eslint/plugin-kit@0.4.1': - dependencies: - '@eslint/core': 0.17.0 - levn: 0.4.1 - '@exodus/bytes@1.15.1(@noble/hashes@2.0.1)': optionalDependencies: '@noble/hashes': 2.0.1 + optional: true '@fastify/busboy@2.1.1': {} @@ -14686,8 +11990,6 @@ snapshots: '@floating-ui/utils@0.2.11': {} - '@fontsource-variable/geist@5.2.9': {} - '@formatjs/intl-localematcher@0.6.2': dependencies: tslib: 2.8.1 @@ -14701,18 +12003,6 @@ snapshots: '@standard-schema/utils': 0.3.0 react-hook-form: 7.66.1(react@19.2.7) - '@humanfs/core@0.19.2': - dependencies: - '@humanfs/types': 0.15.0 - - '@humanfs/node@0.16.8': - dependencies: - '@humanfs/core': 0.19.2 - '@humanfs/types': 0.15.0 - '@humanwhocodes/retry': 0.4.3 - - '@humanfs/types@0.15.0': {} - '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 @@ -14725,8 +12015,6 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} - '@humanwhocodes/retry@0.4.3': {} - '@img/colour@1.0.0': optional: true @@ -14826,14 +12114,6 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/confirm@5.1.21(@types/node@22.19.20)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.20) - '@inquirer/type': 3.0.10(@types/node@22.19.20) - optionalDependencies: - '@types/node': 22.19.20 - optional: true - '@inquirer/confirm@5.1.21(@types/node@24.12.0)': dependencies: '@inquirer/core': 10.3.2(@types/node@24.12.0) @@ -14849,20 +12129,6 @@ snapshots: '@types/node': 25.5.0 optional: true - '@inquirer/core@10.3.2(@types/node@22.19.20)': - dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.20) - cli-width: 4.1.0 - mute-stream: 2.0.0 - signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.20 - optional: true - '@inquirer/core@10.3.2(@types/node@24.12.0)': dependencies: '@inquirer/ansi': 1.0.2 @@ -14899,11 +12165,6 @@ snapshots: '@inquirer/figures@1.0.15': {} - '@inquirer/type@3.0.10(@types/node@22.19.20)': - optionalDependencies: - '@types/node': 22.19.20 - optional: true - '@inquirer/type@3.0.10(@types/node@24.12.0)': optionalDependencies: '@types/node': 24.12.0 @@ -15325,8 +12586,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@mjackson/node-fetch-server@0.2.0': {} - '@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)': dependencies: '@hono/node-server': 1.19.9(hono@4.11.4) @@ -15392,16 +12651,13 @@ snapshots: '@next/env@16.0.10': {} - '@next/env@16.1.7': {} + '@next/env@16.1.7': + optional: true '@next/eslint-plugin-next@15.3.4': dependencies: fast-glob: 3.3.1 - '@next/eslint-plugin-next@16.1.7': - dependencies: - fast-glob: 3.3.1 - '@next/swc-darwin-arm64@16.0.10': optional: true @@ -15483,101 +12739,39 @@ snapshots: '@oozcitak/infra': 2.0.2 '@oozcitak/url': 3.0.0 '@oozcitak/util': 10.0.0 + optional: true '@oozcitak/infra@2.0.2': dependencies: '@oozcitak/util': 10.0.0 + optional: true '@oozcitak/url@3.0.0': dependencies: '@oozcitak/infra': 2.0.2 '@oozcitak/util': 10.0.0 - - '@oozcitak/util@10.0.0': {} - - '@open-draft/deferred-promise@2.2.0': {} - - '@open-draft/logger@0.3.0': - dependencies: - is-node-process: 1.2.0 - outvariant: 1.4.3 - - '@open-draft/until@2.1.0': {} - - '@opentelemetry/api@1.9.0': {} - - '@opentelemetry/semantic-conventions@1.40.0': {} - - '@orama/orama@3.1.16': {} - - '@oxc-parser/binding-android-arm-eabi@0.120.0': - optional: true - - '@oxc-parser/binding-android-arm64@0.120.0': - optional: true - - '@oxc-parser/binding-darwin-arm64@0.120.0': - optional: true - - '@oxc-parser/binding-darwin-x64@0.120.0': - optional: true - - '@oxc-parser/binding-freebsd-x64@0.120.0': - optional: true - - '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': - optional: true - - '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': - optional: true - - '@oxc-parser/binding-linux-arm64-gnu@0.120.0': - optional: true - - '@oxc-parser/binding-linux-arm64-musl@0.120.0': - optional: true - - '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': - optional: true - - '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': - optional: true - - '@oxc-parser/binding-linux-riscv64-musl@0.120.0': - optional: true - - '@oxc-parser/binding-linux-s390x-gnu@0.120.0': - optional: true - - '@oxc-parser/binding-linux-x64-gnu@0.120.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.120.0': + '@oozcitak/util@10.0.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.120.0': - optional: true + '@open-draft/deferred-promise@2.2.0': {} - '@oxc-parser/binding-wasm32-wasi@0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@open-draft/logger@0.3.0': dependencies: - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - optional: true + is-node-process: 1.2.0 + outvariant: 1.4.3 - '@oxc-parser/binding-win32-arm64-msvc@0.120.0': - optional: true + '@open-draft/until@2.1.0': {} - '@oxc-parser/binding-win32-ia32-msvc@0.120.0': - optional: true + '@opentelemetry/api@1.9.0': {} - '@oxc-parser/binding-win32-x64-msvc@0.120.0': - optional: true + '@opentelemetry/semantic-conventions@1.40.0': {} - '@oxc-project/types@0.120.0': {} + '@orama/orama@3.1.16': {} - '@oxc-project/types@0.134.0': {} + '@oxc-project/types@0.134.0': + optional: true '@oxc-resolver/binding-android-arm-eabi@11.19.1': optional: true @@ -15694,7 +12888,8 @@ snapshots: '@oxc-transform/binding-win32-x64-msvc@0.96.0': optional: true - '@package-json/types@0.0.12': {} + '@package-json/types@0.0.12': + optional: true '@playwright/test@1.56.1': dependencies: @@ -15706,12 +12901,6 @@ snapshots: typescript: 5.9.3 optional: true - '@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(typescript@6.0.3)': - optionalDependencies: - prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) - typescript: 6.0.3 - optional: true - '@prisma/config@7.5.0': dependencies: c12: 3.1.0 @@ -15751,29 +12940,6 @@ snapshots: - typescript optional: true - '@prisma/dev@0.20.0(typescript@6.0.3)': - dependencies: - '@electric-sql/pglite': 0.3.15 - '@electric-sql/pglite-socket': 0.0.20(@electric-sql/pglite@0.3.15) - '@electric-sql/pglite-tools': 0.2.20(@electric-sql/pglite@0.3.15) - '@hono/node-server': 1.19.9(hono@4.11.4) - '@mrleebo/prisma-ast': 0.13.1 - '@prisma/get-platform': 7.2.0 - '@prisma/query-plan-executor': 7.2.0 - foreground-child: 3.3.1 - get-port-please: 3.2.0 - hono: 4.11.4 - http-status-codes: 2.3.0 - pathe: 2.0.3 - proper-lockfile: 4.1.2 - remeda: 2.33.4 - std-env: 3.10.0 - valibot: 1.2.0(typescript@6.0.3) - zeptomatch: 2.1.0 - transitivePeerDependencies: - - typescript - optional: true - '@prisma/engines-version@7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e': optional: true @@ -15814,12 +12980,8 @@ snapshots: '@radix-ui/number@1.1.1': {} - '@radix-ui/number@1.1.2': {} - '@radix-ui/primitive@1.1.3': {} - '@radix-ui/primitive@1.1.4': {} - '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -15829,15 +12991,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-accessible-icon@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -15855,23 +13008,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-accordion@1.2.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collapsible': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -15886,20 +13022,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-alert-dialog@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-dialog': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -15909,15 +13031,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-arrow@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -15927,15 +13040,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-aspect-ratio@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.7) @@ -15962,19 +13066,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-avatar@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -15991,22 +13082,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-checkbox@1.3.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16023,22 +13098,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-collapsible@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.7) @@ -16051,30 +13110,12 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-collection@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.14)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16089,19 +13130,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-context-menu@2.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-menu': 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-context@1.1.2(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 @@ -16114,12 +13142,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-context@1.1.4(@types/react@19.2.14)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16142,40 +13164,12 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-dialog@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - aria-hidden: 1.2.6 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-direction@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-direction@1.1.2(@types/react@19.2.14)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16189,19 +13183,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-dismissable-layer@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16217,33 +13198,12 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-dropdown-menu@2.1.17(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-menu': 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.14)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.7) @@ -16255,17 +13215,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-focus-scope@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16280,20 +13229,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-form@0.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-label': 2.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16311,23 +13246,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-hover-card@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-icons@1.3.2(react@19.2.7)': dependencies: react: 19.2.7 @@ -16339,13 +13257,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-id@1.1.2(@types/react@19.2.14)(react@19.2.7)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -16364,15 +13275,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-label@2.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16399,32 +13301,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-menu@2.1.17(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - aria-hidden: 1.2.6 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16443,24 +13319,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-menubar@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-menu': 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16483,28 +13341,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-navigation-menu@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/number': 1.1.1 @@ -16525,26 +13361,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-one-time-password-field@0.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/number': 1.1.2 - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16561,22 +13377,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-password-toggle-field@0.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16595,74 +13395,23 @@ snapshots: aria-hidden: 1.2.6 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - react-remove-scroll: 2.7.1(@types/react@19.2.14)(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-popover@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - aria-hidden: 1.2.6 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/rect': 1.1.1 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-popper@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-arrow': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/rect': 1.1.2 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.1(@types/react@19.2.14)(react@19.2.7) optionalDependencies: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-portal@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/rect': 1.1.1 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: @@ -16689,15 +13438,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.7) @@ -16716,15 +13456,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-primitive@2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.7) @@ -16735,16 +13466,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-progress@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16763,24 +13484,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-radio-group@1.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16798,23 +13501,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-roving-focus@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/number': 1.1.1 @@ -16832,23 +13518,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-scroll-area@1.2.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/number': 1.1.2 - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/number': 1.1.1 @@ -16878,36 +13547,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-select@2.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/number': 1.1.2 - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - aria-hidden: 1.2.6 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -16926,15 +13565,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-separator@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/number': 1.1.1 @@ -16954,25 +13584,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-slider@1.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/number': 1.1.2 - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.7) @@ -16987,13 +13598,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-slot@1.2.5(@types/react@19.2.14)(react@19.2.7)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -17009,21 +13613,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-switch@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -17040,22 +13629,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-tabs@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -17076,26 +13649,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toast@1.2.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -17111,21 +13664,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toggle-group@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toggle': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -17137,17 +13675,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toggle@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -17163,21 +13690,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toolbar@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-separator': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toggle-group': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -17198,38 +13710,12 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-tooltip@1.2.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.14)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.7) @@ -17238,14 +13724,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.14)(react@19.2.7)': - dependencies: - '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.7) @@ -17253,13 +13731,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.14)(react@19.2.7)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.7) @@ -17267,13 +13738,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-escape-keydown@1.1.2(@types/react@19.2.14)(react@19.2.7)': - dependencies: - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 @@ -17281,36 +13745,18 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.14)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.14)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-previous@1.1.2(@types/react@19.2.14)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/rect': 1.1.1 @@ -17318,13 +13764,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.14)(react@19.2.7)': - dependencies: - '@radix-ui/rect': 1.1.2 - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.7) @@ -17332,13 +13771,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-size@1.1.2(@types/react@19.2.14)(react@19.2.7)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -17348,103 +13780,10 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-visually-hidden@1.2.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/rect@1.1.1': {} - '@radix-ui/rect@1.1.2': {} - - '@react-router/dev@7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(tsx@4.22.4)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))(yaml@2.8.2)': - dependencies: - '@babel/core': 7.29.7 - '@babel/generator': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/preset-typescript': 7.28.5(@babel/core@7.29.7) - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 - '@react-router/node': 7.13.1(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) - '@remix-run/node-fetch-server': 0.13.3 - arg: 5.0.2 - babel-dead-code-elimination: 1.0.12 - chokidar: 4.0.3 - dedent: 1.7.0 - es-module-lexer: 1.7.0 - exit-hook: 2.2.1 - isbot: 5.1.42 - jsesc: 3.0.2 - lodash: 4.17.21 - p-map: 7.0.4 - pathe: 1.1.2 - picocolors: 1.1.1 - pkg-types: 2.3.1 - prettier: 3.8.4 - react-refresh: 0.14.2 - react-router: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - semver: 7.8.4 - tinyglobby: 0.2.17 - valibot: 1.2.0(typescript@5.9.3) - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - optionalDependencies: - '@react-router/serve': 7.13.1(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - '@react-router/express@7.13.1(express@4.22.2)(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': - dependencies: - '@react-router/node': 7.13.1(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) - express: 4.22.2 - react-router: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - optionalDependencies: - typescript: 5.9.3 - - '@react-router/node@7.13.1(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': - dependencies: - '@mjackson/node-fetch-server': 0.2.0 - react-router: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - optionalDependencies: - typescript: 5.9.3 - - '@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': - dependencies: - '@mjackson/node-fetch-server': 0.2.0 - '@react-router/express': 7.13.1(express@4.22.2)(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) - '@react-router/node': 7.13.1(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) - compression: 1.8.1 - express: 4.22.2 - get-port: 5.1.1 - morgan: 1.11.0 - react-router: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - source-map-support: 0.5.21 - transitivePeerDependencies: - - supports-color - - typescript - '@remirror/core-constants@3.0.0': {} - '@remix-run/node-fetch-server@0.13.3': {} - '@rolldown/binding-android-arm64@1.1.0': optional: true @@ -17494,11 +13833,11 @@ snapshots: '@rolldown/binding-win32-x64-msvc@1.1.0': optional: true - '@rolldown/pluginutils@1.0.0-beta.40': {} - - '@rolldown/pluginutils@1.0.0-rc.3': {} + '@rolldown/pluginutils@1.0.0-beta.40': + optional: true - '@rolldown/pluginutils@1.0.1': {} + '@rolldown/pluginutils@1.0.1': + optional: true '@rollup/plugin-alias@5.1.1(rollup@4.53.2)': optionalDependencies: @@ -18000,46 +14339,12 @@ snapshots: '@smithy/util-waiter@4.2.13': dependencies: '@smithy/abort-controller': 4.2.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@smithy/uuid@1.1.2': - dependencies: - tslib: 2.8.1 - - '@solid-primitives/event-listener@2.4.5(solid-js@1.9.12)': - dependencies: - '@solid-primitives/utils': 6.4.0(solid-js@1.9.12) - solid-js: 1.9.12 - - '@solid-primitives/keyboard@1.3.5(solid-js@1.9.12)': - dependencies: - '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.12) - '@solid-primitives/rootless': 1.5.3(solid-js@1.9.12) - '@solid-primitives/utils': 6.4.0(solid-js@1.9.12) - solid-js: 1.9.12 - - '@solid-primitives/resize-observer@2.1.5(solid-js@1.9.12)': - dependencies: - '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.12) - '@solid-primitives/rootless': 1.5.3(solid-js@1.9.12) - '@solid-primitives/static-store': 0.1.3(solid-js@1.9.12) - '@solid-primitives/utils': 6.4.0(solid-js@1.9.12) - solid-js: 1.9.12 - - '@solid-primitives/rootless@1.5.3(solid-js@1.9.12)': - dependencies: - '@solid-primitives/utils': 6.4.0(solid-js@1.9.12) - solid-js: 1.9.12 - - '@solid-primitives/static-store@0.1.3(solid-js@1.9.12)': - dependencies: - '@solid-primitives/utils': 6.4.0(solid-js@1.9.12) - solid-js: 1.9.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 - '@solid-primitives/utils@6.4.0(solid-js@1.9.12)': + '@smithy/uuid@1.1.2': dependencies: - solid-js: 1.9.12 + tslib: 2.8.1 '@stackblitz/sdk@1.11.0': {} @@ -18049,16 +14354,6 @@ snapshots: '@standard-schema/utils@0.3.0': {} - '@stylistic/eslint-plugin@5.10.0(eslint@9.39.4(jiti@2.6.1))': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/types': 8.61.0 - eslint: 9.39.4(jiti@2.6.1) - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - estraverse: 5.3.0 - picomatch: 4.0.4 - '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -18073,88 +14368,42 @@ snapshots: source-map-js: 1.2.1 tailwindcss: 4.2.2 - '@tailwindcss/node@4.3.0': - dependencies: - '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.23.0 - jiti: 2.6.1 - lightningcss: 1.32.0 - magic-string: 0.30.21 - source-map-js: 1.2.1 - tailwindcss: 4.3.0 - '@tailwindcss/oxide-android-arm64@4.2.2': optional: true - '@tailwindcss/oxide-android-arm64@4.3.0': - optional: true - '@tailwindcss/oxide-darwin-arm64@4.2.2': optional: true - '@tailwindcss/oxide-darwin-arm64@4.3.0': - optional: true - '@tailwindcss/oxide-darwin-x64@4.2.2': optional: true - '@tailwindcss/oxide-darwin-x64@4.3.0': - optional: true - '@tailwindcss/oxide-freebsd-x64@4.2.2': optional: true - '@tailwindcss/oxide-freebsd-x64@4.3.0': - optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': - optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': - optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.3.0': - optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.2.2': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.3.0': - optional: true - '@tailwindcss/oxide-linux-x64-musl@4.2.2': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.3.0': - optional: true - '@tailwindcss/oxide-wasm32-wasi@4.2.2': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.3.0': - optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': - optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.2.2': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.3.0': - optional: true - '@tailwindcss/oxide@4.2.2': optionalDependencies: '@tailwindcss/oxide-android-arm64': 4.2.2 @@ -18170,21 +14419,6 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 - '@tailwindcss/oxide@4.3.0': - optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.3.0 - '@tailwindcss/oxide-darwin-arm64': 4.3.0 - '@tailwindcss/oxide-darwin-x64': 4.3.0 - '@tailwindcss/oxide-freebsd-x64': 4.3.0 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.0 - '@tailwindcss/oxide-linux-arm64-gnu': 4.3.0 - '@tailwindcss/oxide-linux-arm64-musl': 4.3.0 - '@tailwindcss/oxide-linux-x64-gnu': 4.3.0 - '@tailwindcss/oxide-linux-x64-musl': 4.3.0 - '@tailwindcss/oxide-wasm32-wasi': 4.3.0 - '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 - '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 - '@tailwindcss/postcss@4.2.2': dependencies: '@alloc/quick-lru': 5.2.0 @@ -18198,135 +14432,16 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 4.2.2 - '@tailwindcss/vite@4.3.0(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': - dependencies: - '@tailwindcss/node': 4.3.0 - '@tailwindcss/oxide': 4.3.0 - tailwindcss: 4.3.0 - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - - '@tanstack/devtools-client@0.0.6': - dependencies: - '@tanstack/devtools-event-client': 0.4.3 - - '@tanstack/devtools-event-bus@0.4.1': - dependencies: - ws: 8.21.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@tanstack/devtools-event-client@0.4.3': {} - - '@tanstack/devtools-ui@0.5.2(csstype@3.2.3)(solid-js@1.9.12)': - dependencies: - clsx: 2.1.1 - dayjs: 1.11.21 - goober: 2.1.19(csstype@3.2.3) - solid-js: 1.9.12 - transitivePeerDependencies: - - csstype - - '@tanstack/devtools-vite@0.7.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': - dependencies: - '@tanstack/devtools-client': 0.0.6 - '@tanstack/devtools-event-bus': 0.4.1 - chalk: 5.6.2 - launch-editor: 2.14.1 - magic-string: 0.30.21 - oxc-parser: 0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - picomatch: 4.0.4 - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - - bufferutil - - utf-8-validate - - '@tanstack/devtools@0.12.2(csstype@3.2.3)(solid-js@1.9.12)': - dependencies: - '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.12) - '@solid-primitives/keyboard': 1.3.5(solid-js@1.9.12) - '@solid-primitives/resize-observer': 2.1.5(solid-js@1.9.12) - '@tanstack/devtools-client': 0.0.6 - '@tanstack/devtools-event-bus': 0.4.1 - '@tanstack/devtools-ui': 0.5.2(csstype@3.2.3)(solid-js@1.9.12) - clsx: 2.1.1 - goober: 2.1.19(csstype@3.2.3) - solid-js: 1.9.12 - transitivePeerDependencies: - - bufferutil - - csstype - - utf-8-validate - - '@tanstack/eslint-config@0.4.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@eslint/js': 10.0.1(eslint@9.39.4(jiti@2.6.1)) - '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.6.1)) - eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-n: 17.24.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - globals: 17.6.0 - typescript-eslint: 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - vue-eslint-parser: 10.4.1(eslint@9.39.4(jiti@2.6.1)) - transitivePeerDependencies: - - '@typescript-eslint/utils' - - eslint-import-resolver-node - - supports-color - - typescript - - '@tanstack/history@1.161.6': {} + '@tanstack/history@1.161.6': + optional: true '@tanstack/query-core@5.90.10': {} - '@tanstack/query-devtools@5.101.0': {} - - '@tanstack/react-devtools@0.10.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)': - dependencies: - '@tanstack/devtools': 0.12.2(csstype@3.2.3)(solid-js@1.9.12) - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - transitivePeerDependencies: - - bufferutil - - csstype - - solid-js - - utf-8-validate - - '@tanstack/react-query-devtools@5.101.0(@tanstack/react-query@5.90.10(react@19.2.7))(react@19.2.7)': - dependencies: - '@tanstack/query-devtools': 5.101.0 - '@tanstack/react-query': 5.90.10(react@19.2.7) - react: 19.2.7 - '@tanstack/react-query@5.90.10(react@19.2.7)': dependencies: '@tanstack/query-core': 5.90.10 react: 19.2.7 - '@tanstack/react-router-devtools@1.167.0(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.168.9)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-devtools-core': 1.168.0(@tanstack/router-core@1.168.9)(csstype@3.2.3) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@tanstack/router-core': 1.168.9 - transitivePeerDependencies: - - csstype - - '@tanstack/react-router-ssr-query@1.167.1(@tanstack/query-core@5.90.10)(@tanstack/react-query@5.90.10(react@19.2.7))(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.168.9)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@tanstack/query-core': 5.90.10 - '@tanstack/react-query': 5.90.10(react@19.2.7) - '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-ssr-query-core': 1.169.1(@tanstack/query-core@5.90.10)(@tanstack/router-core@1.168.9) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - transitivePeerDependencies: - - '@tanstack/router-core' - '@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@tanstack/history': 1.161.6 @@ -18335,6 +14450,7 @@ snapshots: isbot: 5.1.42 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) + optional: true '@tanstack/react-start-client@1.166.25(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: @@ -18343,6 +14459,7 @@ snapshots: '@tanstack/start-client-core': 1.167.9 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) + optional: true '@tanstack/react-start-server@1.166.25(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: @@ -18355,26 +14472,7 @@ snapshots: react-dom: 19.2.7(react@19.2.7) transitivePeerDependencies: - crossws - - '@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': - dependencies: - '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/react-start-client': 1.166.25(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/react-start-server': 1.166.25(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-utils': 1.162.2 - '@tanstack/start-client-core': 1.167.9 - '@tanstack/start-plugin-core': 1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.6(srvx@0.11.16))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - '@tanstack/start-server-core': 1.167.9(crossws@0.4.6(srvx@0.11.16)) - pathe: 2.0.3 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - transitivePeerDependencies: - - '@rsbuild/core' - - crossws - - supports-color - - vite-plugin-solid - - webpack + optional: true '@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: @@ -18397,33 +14495,13 @@ snapshots: - webpack optional: true - '@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': - dependencies: - '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/react-start-client': 1.166.25(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/react-start-server': 1.166.25(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-utils': 1.162.2 - '@tanstack/start-client-core': 1.167.9 - '@tanstack/start-plugin-core': 1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.6(srvx@0.11.16))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - '@tanstack/start-server-core': 1.167.9(crossws@0.4.6(srvx@0.11.16)) - pathe: 2.0.3 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - transitivePeerDependencies: - - '@rsbuild/core' - - crossws - - supports-color - - vite-plugin-solid - - webpack - optional: true - '@tanstack/react-store@0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@tanstack/store': 0.9.3 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) use-sync-external-store: 1.6.0(react@19.2.7) + optional: true '@tanstack/router-core@1.168.9': dependencies: @@ -18431,14 +14509,7 @@ snapshots: cookie-es: 2.0.1 seroval: 1.5.4 seroval-plugins: 1.5.4(seroval@1.5.4) - - '@tanstack/router-devtools-core@1.168.0(@tanstack/router-core@1.168.9)(csstype@3.2.3)': - dependencies: - '@tanstack/router-core': 1.168.9 - clsx: 2.1.1 - goober: 2.1.19(csstype@3.2.3) - optionalDependencies: - csstype: 3.2.3 + optional: true '@tanstack/router-generator@1.166.24': dependencies: @@ -18452,27 +14523,7 @@ snapshots: zod: 3.25.76 transitivePeerDependencies: - supports-color - - '@tanstack/router-plugin@1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': - dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) - '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 - '@tanstack/router-core': 1.168.9 - '@tanstack/router-generator': 1.166.24 - '@tanstack/router-utils': 1.161.6 - '@tanstack/virtual-file-routes': 1.161.7 - chokidar: 3.6.0 - unplugin: 2.3.11 - zod: 3.25.76 - optionalDependencies: - '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color + optional: true '@tanstack/router-plugin@1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: @@ -18496,33 +14547,6 @@ snapshots: - supports-color optional: true - '@tanstack/router-plugin@1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': - dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) - '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 - '@tanstack/router-core': 1.168.9 - '@tanstack/router-generator': 1.166.24 - '@tanstack/router-utils': 1.161.6 - '@tanstack/virtual-file-routes': 1.161.7 - chokidar: 3.6.0 - unplugin: 2.3.11 - zod: 3.25.76 - optionalDependencies: - '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color - optional: true - - '@tanstack/router-ssr-query-core@1.169.1(@tanstack/query-core@5.90.10)(@tanstack/router-core@1.168.9)': - dependencies: - '@tanstack/query-core': 5.90.10 - '@tanstack/router-core': 1.168.9 - '@tanstack/router-utils@1.161.6': dependencies: '@babel/core': 7.29.7 @@ -18536,6 +14560,7 @@ snapshots: tinyglobby: 0.2.17 transitivePeerDependencies: - supports-color + optional: true '@tanstack/router-utils@1.162.2': dependencies: @@ -18549,6 +14574,7 @@ snapshots: tinyglobby: 0.2.17 transitivePeerDependencies: - supports-color + optional: true '@tanstack/start-client-core@1.167.9': dependencies: @@ -18556,75 +14582,12 @@ snapshots: '@tanstack/start-fn-stubs': 1.161.6 '@tanstack/start-storage-context': 1.166.23 seroval: 1.5.4 + optional: true - '@tanstack/start-fn-stubs@1.161.6': {} - - '@tanstack/start-plugin-core@1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.6(srvx@0.11.16))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/core': 7.29.7 - '@babel/types': 7.29.7 - '@rolldown/pluginutils': 1.0.0-beta.40 - '@tanstack/router-core': 1.168.9 - '@tanstack/router-generator': 1.166.24 - '@tanstack/router-plugin': 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - '@tanstack/router-utils': 1.161.6 - '@tanstack/start-client-core': 1.167.9 - '@tanstack/start-server-core': 1.167.9(crossws@0.4.6(srvx@0.11.16)) - cheerio: 1.2.0 - exsolve: 1.0.8 - pathe: 2.0.3 - picomatch: 4.0.4 - source-map: 0.7.6 - srvx: 0.11.16 - tinyglobby: 0.2.17 - ufo: 1.6.4 - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - vitefu: 1.1.3(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - xmlbuilder2: 4.0.3 - zod: 3.25.76 - transitivePeerDependencies: - - '@rsbuild/core' - - '@tanstack/react-router' - - crossws - - supports-color - - vite-plugin-solid - - webpack - - '@tanstack/start-plugin-core@1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.6(srvx@0.11.16))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/core': 7.29.7 - '@babel/types': 7.29.7 - '@rolldown/pluginutils': 1.0.0-beta.40 - '@tanstack/router-core': 1.168.9 - '@tanstack/router-generator': 1.166.24 - '@tanstack/router-plugin': 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) - '@tanstack/router-utils': 1.161.6 - '@tanstack/start-client-core': 1.167.9 - '@tanstack/start-server-core': 1.167.9(crossws@0.4.6(srvx@0.11.16)) - cheerio: 1.2.0 - exsolve: 1.0.8 - pathe: 2.0.3 - picomatch: 4.0.4 - source-map: 0.7.6 - srvx: 0.11.16 - tinyglobby: 0.2.17 - ufo: 1.6.4 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) - vitefu: 1.1.3(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) - xmlbuilder2: 4.0.3 - zod: 3.25.76 - transitivePeerDependencies: - - '@rsbuild/core' - - '@tanstack/react-router' - - crossws - - supports-color - - vite-plugin-solid - - webpack + '@tanstack/start-fn-stubs@1.161.6': optional: true - '@tanstack/start-plugin-core@1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.6(srvx@0.11.16))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': + '@tanstack/start-plugin-core@1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.6(srvx@0.11.16))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.29.7 @@ -18632,7 +14595,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.40 '@tanstack/router-core': 1.168.9 '@tanstack/router-generator': 1.166.24 - '@tanstack/router-plugin': 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + '@tanstack/router-plugin': 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) '@tanstack/router-utils': 1.161.6 '@tanstack/start-client-core': 1.167.9 '@tanstack/start-server-core': 1.167.9(crossws@0.4.6(srvx@0.11.16)) @@ -18644,8 +14607,8 @@ snapshots: srvx: 0.11.16 tinyglobby: 0.2.17 ufo: 1.6.4 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - vitefu: 1.1.3(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vitefu: 1.1.3(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) xmlbuilder2: 4.0.3 zod: 3.25.76 transitivePeerDependencies: @@ -18667,35 +14630,18 @@ snapshots: seroval: 1.5.4 transitivePeerDependencies: - crossws + optional: true '@tanstack/start-storage-context@1.166.23': dependencies: '@tanstack/router-core': 1.168.9 + optional: true - '@tanstack/store@0.9.3': {} - - '@tanstack/virtual-file-routes@1.161.7': {} - - '@testing-library/dom@10.4.1': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/runtime': 7.29.2 - '@types/aria-query': 5.0.4 - aria-query: 5.3.0 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - picocolors: 1.1.1 - pretty-format: 27.5.1 + '@tanstack/store@0.9.3': + optional: true - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@babel/runtime': 7.29.2 - '@testing-library/dom': 10.4.1 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@tanstack/virtual-file-routes@1.161.7': + optional: true '@tiptap/core@3.20.0(@tiptap/pm@3.15.3)': dependencies: @@ -18976,29 +14922,6 @@ snapshots: tslib: 2.8.1 optional: true - '@types/aria-query@5.0.4': {} - - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 - - '@types/babel__generator@7.27.0': - dependencies: - '@babel/types': 7.29.7 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 - - '@types/babel__traverse@7.28.0': - dependencies: - '@babel/types': 7.29.7 - '@types/bun@1.3.2(@types/react@19.2.14)': dependencies: bun-types: 1.3.2(@types/react@19.2.14) @@ -19020,8 +14943,6 @@ snapshots: dependencies: diff: 8.0.4 - '@types/esrecurse@4.3.1': {} - '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.8 @@ -19042,8 +14963,6 @@ snapshots: '@types/through': 0.0.33 rxjs: 6.6.7 - '@types/json-schema@7.0.15': {} - '@types/json5@0.0.29': {} '@types/katex@0.16.7': {} @@ -19079,10 +14998,6 @@ snapshots: dependencies: undici-types: 6.21.0 - '@types/node@22.19.20': - dependencies: - undici-types: 6.21.0 - '@types/node@24.0.3': dependencies: undici-types: 7.8.0 @@ -19158,38 +15073,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/type-utils': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.61.0 - eslint: 9.39.4(jiti@2.6.1) - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/type-utils': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.0 - eslint: 9.39.4(jiti@2.6.1) - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@6.0.3) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.58.0 @@ -19202,30 +15085,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.61.0 - debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.0 - debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/project-service@8.58.0(typescript@5.9.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.58.0(typescript@5.9.3) @@ -19243,15 +15102,7 @@ snapshots: typescript: 5.9.3 transitivePeerDependencies: - supports-color - - '@typescript-eslint/project-service@8.61.0(typescript@6.0.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@6.0.3) - '@typescript-eslint/types': 8.61.0 - debug: 4.4.3 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color + optional: true '@typescript-eslint/scope-manager@8.58.0': dependencies: @@ -19262,6 +15113,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.61.0 '@typescript-eslint/visitor-keys': 8.61.0 + optional: true '@typescript-eslint/tsconfig-utils@8.58.0(typescript@5.9.3)': dependencies: @@ -19270,10 +15122,7 @@ snapshots: '@typescript-eslint/tsconfig-utils@8.61.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - - '@typescript-eslint/tsconfig-utils@8.61.0(typescript@6.0.3)': - dependencies: - typescript: 6.0.3 + optional: true '@typescript-eslint/type-utils@8.58.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: @@ -19287,33 +15136,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/type-utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - ts-api-utils: 2.5.0(typescript@6.0.3) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/types@8.58.0': {} - '@typescript-eslint/types@8.61.0': {} + '@typescript-eslint/types@8.61.0': + optional: true '@typescript-eslint/typescript-estree@8.58.0(typescript@5.9.3)': dependencies: @@ -19344,21 +15170,7 @@ snapshots: typescript: 5.9.3 transitivePeerDependencies: - supports-color - - '@typescript-eslint/typescript-estree@8.61.0(typescript@6.0.3)': - dependencies: - '@typescript-eslint/project-service': 8.61.0(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@6.0.3) - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/visitor-keys': 8.61.0 - debug: 4.4.3 - minimatch: 10.2.5 - semver: 7.8.4 - tinyglobby: 0.2.17 - ts-api-utils: 2.5.0(typescript@6.0.3) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color + optional: true '@typescript-eslint/utils@8.58.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: @@ -19383,28 +15195,6 @@ snapshots: - supports-color optional: true - '@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@5.9.3) - eslint: 9.39.4(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) - eslint: 9.39.4(jiti@2.6.1) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/visitor-keys@8.58.0': dependencies: '@typescript-eslint/types': 8.58.0 @@ -19414,6 +15204,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.61.0 eslint-visitor-keys: 5.0.1 + optional: true '@ungap/structured-clone@1.3.0': {} @@ -19555,7 +15346,7 @@ snapshots: '@vercel/analytics@1.6.1(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(vue@3.5.24(typescript@5.9.3))': optionalDependencies: - next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 vue: 3.5.24(typescript@5.9.3) @@ -19569,18 +15360,6 @@ snapshots: '@vercel/oidc@3.0.3': {} - '@vitejs/plugin-react@5.2.0(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': - dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) - '@rolldown/pluginutils': 1.0.0-rc.3 - '@types/babel__core': 7.20.5 - react-refresh: 0.18.0 - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color - '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.3 @@ -19589,15 +15368,6 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/expect@4.1.8': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 - chai: 6.2.2 - tinyrainbow: 3.1.0 - '@vitest/mocker@3.2.4(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 @@ -19625,85 +15395,32 @@ snapshots: msw: 2.12.10(@types/node@25.5.0)(typescript@5.9.3) vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@4.1.8(msw@2.12.10(@types/node@22.19.20)(typescript@5.9.3))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': - dependencies: - '@vitest/spy': 4.1.8 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - msw: 2.12.10(@types/node@22.19.20)(typescript@5.9.3) - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - optional: true - - '@vitest/mocker@4.1.8(msw@2.12.10(@types/node@22.19.20)(typescript@6.0.3))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': - dependencies: - '@vitest/spy': 4.1.8 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - msw: 2.12.10(@types/node@22.19.20)(typescript@6.0.3) - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - - '@vitest/mocker@4.1.8(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': - dependencies: - '@vitest/spy': 4.1.8 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - msw: 2.12.10(@types/node@25.5.0)(typescript@5.9.3) - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - optional: true - '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 - '@vitest/pretty-format@4.1.8': - dependencies: - tinyrainbow: 3.1.0 - '@vitest/runner@3.2.4': dependencies: '@vitest/utils': 3.2.4 pathe: 2.0.3 strip-literal: 3.1.0 - '@vitest/runner@4.1.8': - dependencies: - '@vitest/utils': 4.1.8 - pathe: 2.0.3 - '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/snapshot@4.1.8': - dependencies: - '@vitest/pretty-format': 4.1.8 - '@vitest/utils': 4.1.8 - magic-string: 0.30.21 - pathe: 2.0.3 - '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.4 - '@vitest/spy@4.1.8': {} - '@vitest/utils@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 loupe: 3.2.1 tinyrainbow: 2.0.0 - '@vitest/utils@4.1.8': - dependencies: - '@vitest/pretty-format': 4.1.8 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 - '@vue/compiler-core@3.5.24': dependencies: '@babel/parser': 7.29.7 @@ -19756,20 +15473,8 @@ snapshots: '@vue/shared': 3.5.24 vue: 3.5.24(typescript@5.9.3) - '@vue/server-renderer@3.5.24(vue@3.5.24(typescript@6.0.3))': - dependencies: - '@vue/compiler-ssr': 3.5.24 - '@vue/shared': 3.5.24 - vue: 3.5.24(typescript@6.0.3) - optional: true - '@vue/shared@3.5.24': {} - accepts@1.3.8: - dependencies: - mime-types: 2.1.35 - negotiator: 0.6.3 - accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -19779,10 +15484,6 @@ snapshots: dependencies: acorn: 8.15.0 - acorn-jsx@5.3.2(acorn@8.16.0): - dependencies: - acorn: 8.16.0 - acorn-walk@8.3.4: dependencies: acorn: 8.15.0 @@ -19840,11 +15541,10 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - ansis@4.2.0: {} - ansis@4.3.1: {} + ansis@4.3.1: + optional: true anymatch@3.1.3: dependencies: @@ -19853,8 +15553,6 @@ snapshots: arg@4.1.3: {} - arg@5.0.2: {} - argparse@2.0.1: {} args-tokenizer@0.3.0: {} @@ -19863,10 +15561,6 @@ snapshots: dependencies: tslib: 2.8.1 - aria-query@5.3.0: - dependencies: - dequal: 2.0.3 - aria-query@5.3.2: {} array-buffer-byte-length@1.0.2: @@ -19874,8 +15568,6 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 - array-flatten@1.1.1: {} - array-includes@3.1.9: dependencies: call-bind: 1.0.8 @@ -19974,149 +15666,41 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - aws-ssl-profiles@1.1.2: - optional: true - - axe-core@4.11.0: {} - - axobject-query@4.1.0: {} - - babel-dead-code-elimination@1.0.12: - dependencies: - '@babel/core': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 - transitivePeerDependencies: - - supports-color - - babel-plugin-react-compiler@1.0.0: - dependencies: - '@babel/types': 7.29.7 - optional: true - - bail@2.0.2: {} - - balanced-match@1.0.2: {} - - balanced-match@4.0.4: {} - - base64-js@1.5.1: {} - - baseline-browser-mapping@2.10.35: {} - - basic-auth@2.0.1: - dependencies: - safe-buffer: 5.1.2 - - basic-ftp@5.0.5: {} - - better-auth@1.6.16(1ab4f2557a8135537aff00ad0ec4ded1): - dependencies: - '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - '@better-auth/drizzle-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1) - '@better-auth/kysely-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(kysely@0.29.2) - '@better-auth/memory-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1) - '@better-auth/mongo-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(mongodb@6.21.0(socks@2.8.7)) - '@better-auth/prisma-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(typescript@6.0.3))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)) - '@better-auth/telemetry': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2) - '@better-auth/utils': 0.4.1 - '@better-fetch/fetch': 1.2.2 - '@noble/ciphers': 2.1.1 - '@noble/hashes': 2.0.1 - better-call: 1.3.6(zod@4.4.3) - defu: 6.1.7 - jose: 6.2.0 - kysely: 0.29.2 - nanostores: 1.1.1 - zod: 4.4.3 - optionalDependencies: - '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(typescript@6.0.3) - '@tanstack/react-start': 1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - mongodb: 6.21.0(socks@2.8.7) - mysql2: 3.15.3 - next: 16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - solid-js: 1.9.12 - vitest: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@22.19.20)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@22.19.20)(typescript@6.0.3))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - vue: 3.5.24(typescript@6.0.3) - transitivePeerDependencies: - - '@cloudflare/workers-types' - - '@opentelemetry/api' + aws-ssl-profiles@1.1.2: + optional: true + + axe-core@4.11.0: {} - better-auth@1.6.16(21c5674fc5bfa4beefd02b4684417e5e): + axobject-query@4.1.0: {} + + babel-dead-code-elimination@1.0.12: dependencies: - '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - '@better-auth/drizzle-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1) - '@better-auth/kysely-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(kysely@0.29.2) - '@better-auth/memory-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1) - '@better-auth/mongo-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(mongodb@6.21.0(socks@2.8.7)) - '@better-auth/prisma-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3)) - '@better-auth/telemetry': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2) - '@better-auth/utils': 0.4.1 - '@better-fetch/fetch': 1.2.2 - '@noble/ciphers': 2.1.1 - '@noble/hashes': 2.0.1 - better-call: 1.3.6(zod@4.4.3) - defu: 6.1.7 - jose: 6.2.0 - kysely: 0.29.2 - nanostores: 1.1.1 - zod: 4.4.3 - optionalDependencies: - '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3) - '@tanstack/react-start': 1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - mongodb: 6.21.0(socks@2.8.7) - mysql2: 3.15.3 - next: 16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - solid-js: 1.9.12 - vitest: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@22.19.20)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@22.19.20)(typescript@5.9.3))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - vue: 3.5.24(typescript@5.9.3) + '@babel/core': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 transitivePeerDependencies: - - '@cloudflare/workers-types' - - '@opentelemetry/api' + - supports-color + optional: true - better-auth@1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)): + babel-plugin-react-compiler@1.0.0: dependencies: - '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - '@better-auth/drizzle-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1) - '@better-auth/kysely-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(kysely@0.29.2) - '@better-auth/memory-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1) - '@better-auth/mongo-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(mongodb@6.21.0(socks@2.8.7)) - '@better-auth/prisma-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3)) - '@better-auth/telemetry': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2) - '@better-auth/utils': 0.4.1 - '@better-fetch/fetch': 1.2.2 - '@noble/ciphers': 2.1.1 - '@noble/hashes': 2.0.1 - better-call: 1.3.6(zod@4.4.3) - defu: 6.1.7 - jose: 6.2.0 - kysely: 0.29.2 - nanostores: 1.1.1 - zod: 4.4.3 - optionalDependencies: - '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3) - '@tanstack/react-start': 1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) - mongodb: 6.21.0(socks@2.8.7) - mysql2: 3.15.3 - next: 16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - solid-js: 1.9.12 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) - vue: 3.5.24(typescript@5.9.3) - transitivePeerDependencies: - - '@cloudflare/workers-types' - - '@opentelemetry/api' + '@babel/types': 7.29.7 + optional: true + + bail@2.0.2: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.10.35: {} + + basic-ftp@5.0.5: {} - better-auth@1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@4.1.8(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)))(vue@3.5.24(typescript@5.9.3)): + better-auth@1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)): dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) '@better-auth/drizzle-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1) @@ -20137,7 +15721,7 @@ snapshots: zod: 4.4.3 optionalDependencies: '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3) - '@tanstack/react-start': 1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + '@tanstack/react-start': 1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) mongodb: 6.21.0(socks@2.8.7) mysql2: 3.15.3 next: 16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -20145,7 +15729,7 @@ snapshots: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) solid-js: 1.9.12 - vitest: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) vue: 3.5.24(typescript@5.9.3) transitivePeerDependencies: - '@cloudflare/workers-types' @@ -20163,6 +15747,7 @@ snapshots: bidi-js@1.0.3: dependencies: require-from-string: 2.0.2 + optional: true binary-extensions@2.3.0: {} @@ -20172,23 +15757,6 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@1.20.5: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.1 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.15.2 - raw-body: 2.5.3 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - body-parser@2.2.2: dependencies: bytes: 3.1.2 @@ -20243,8 +15811,6 @@ snapshots: bson@6.10.4: optional: true - buffer-from@1.1.2: {} - buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -20355,8 +15921,6 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 - chai@6.2.2: {} - chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -20418,6 +15982,7 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.2.2 + optional: true cheerio@1.2.0: dependencies: @@ -20432,6 +15997,7 @@ snapshots: parse5-parser-stream: 7.1.2 undici: 7.27.2 whatwg-mimetype: 4.0.0 + optional: true chevrotain@10.5.0: dependencies: @@ -20545,26 +16111,11 @@ snapshots: commander@8.3.0: {} - comment-parser@1.4.7: {} + comment-parser@1.4.7: + optional: true commondir@1.0.1: {} - compressible@2.0.18: - dependencies: - mime-db: 1.54.0 - - compression@1.8.1: - dependencies: - bytes: 3.1.2 - compressible: 2.0.18 - debug: 2.6.9 - negotiator: 0.6.4 - on-headers: 1.1.0 - safe-buffer: 5.2.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - compute-scroll-into-view@3.1.1: {} concat-map@0.0.1: {} @@ -20580,19 +16131,14 @@ snapshots: snake-case: 2.1.0 upper-case: 1.1.3 - content-disposition@0.5.4: - dependencies: - safe-buffer: 5.2.1 - content-disposition@1.0.1: {} content-type@1.0.5: {} convert-source-map@2.0.0: {} - cookie-es@2.0.1: {} - - cookie-signature@1.0.7: {} + cookie-es@2.0.1: + optional: true cookie-signature@1.2.2: {} @@ -20616,15 +16162,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - cosmiconfig@9.0.1(typescript@6.0.3): - dependencies: - env-paths: 2.2.1 - import-fresh: 3.3.1 - js-yaml: 4.2.0 - parse-json: 5.2.0 - optionalDependencies: - typescript: 6.0.3 - create-require@1.1.1: {} crelt@1.0.6: {} @@ -20638,6 +16175,7 @@ snapshots: crossws@0.4.6(srvx@0.11.16): optionalDependencies: srvx: 0.11.16 + optional: true css-declaration-sorter@7.3.0(postcss@8.5.6): dependencies: @@ -20719,6 +16257,7 @@ snapshots: '@csstools/css-syntax-patches-for-csstree': 1.1.5(css-tree@3.2.1) css-tree: 3.2.1 lru-cache: 11.5.1 + optional: true csstype@3.2.3: {} @@ -20734,6 +16273,7 @@ snapshots: whatwg-url: 16.0.1(@noble/hashes@2.0.1) transitivePeerDependencies: - '@noble/hashes' + optional: true data-view-buffer@1.0.2: dependencies: @@ -20757,17 +16297,6 @@ snapshots: date-fns@4.1.0: {} - dayjs@1.11.21: {} - - db0@0.3.4(@electric-sql/pglite@0.3.15)(mysql2@3.15.3): - optionalDependencies: - '@electric-sql/pglite': 0.3.15 - mysql2: 3.15.3 - - debug@2.6.9: - dependencies: - ms: 2.0.0 - debug@3.2.7: dependencies: ms: 2.1.3 @@ -20776,7 +16305,8 @@ snapshots: dependencies: ms: 2.1.3 - decimal.js@10.6.0: {} + decimal.js@10.6.0: + optional: true decode-named-character-reference@1.2.0: dependencies: @@ -20854,8 +16384,6 @@ snapshots: destr@2.0.5: {} - destroy@1.2.0: {} - detect-libc@2.1.2: {} detect-node-es@1.1.0: {} @@ -20880,8 +16408,6 @@ snapshots: dependencies: esutils: 2.0.3 - dom-accessibility-api@0.5.16: {} - dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -20964,34 +16490,25 @@ snapshots: dependencies: iconv-lite: 0.6.3 whatwg-encoding: 3.1.1 + optional: true enhanced-resolve@5.20.1: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 - enhanced-resolve@5.23.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.3 - entities@4.5.0: {} entities@6.0.1: {} - entities@7.0.1: {} + entities@7.0.1: + optional: true - entities@8.0.0: {} + entities@8.0.0: + optional: true env-paths@2.2.1: {} - env-runner@0.1.12: - dependencies: - crossws: 0.4.6(srvx@0.11.16) - exsolve: 1.0.8 - httpxy: 0.5.3 - srvx: 0.11.16 - error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -21078,8 +16595,6 @@ snapshots: es-module-lexer@1.7.0: {} - es-module-lexer@2.1.0: {} - es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -21201,6 +16716,7 @@ snapshots: '@esbuild/win32-arm64': 0.28.0 '@esbuild/win32-ia32': 0.28.0 '@esbuild/win32-x64': 0.28.0 + optional: true escalade@3.2.0: {} @@ -21220,11 +16736,6 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-compat-utils@0.5.1(eslint@9.39.4(jiti@2.6.1)): - dependencies: - eslint: 9.39.4(jiti@2.6.1) - semver: 7.8.4 - eslint-config-next@15.3.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 15.3.4 @@ -21245,32 +16756,13 @@ snapshots: - eslint-plugin-import-x - supports-color - eslint-config-next@16.1.7(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): - dependencies: - '@next/eslint-plugin-next': 16.1.7 - eslint: 9.39.4(jiti@2.6.1) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-react-hooks: 7.1.1(eslint@9.39.4(jiti@2.6.1)) - globals: 16.4.0 - typescript-eslint: 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-webpack - - eslint-plugin-import-x - - supports-color - eslint-import-context@0.1.9(unrs-resolver@1.12.2): dependencies: get-tsconfig: 4.14.0 stable-hash-x: 0.2.0 optionalDependencies: unrs-resolver: 1.12.2 + optional: true eslint-import-resolver-node@0.3.9: dependencies: @@ -21296,22 +16788,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)): - dependencies: - '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - get-tsconfig: 4.13.0 - is-bun-module: 2.0.0 - stable-hash: 0.0.5 - tinyglobby: 0.2.15 - unrs-resolver: 1.11.1 - optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)) - transitivePeerDependencies: - - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: debug: 3.2.7 @@ -21323,24 +16799,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.4(jiti@2.6.1) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) - transitivePeerDependencies: - - supports-color - - eslint-plugin-es-x@7.8.0(eslint@9.39.4(jiti@2.6.1)): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.2 - eslint: 9.39.4(jiti@2.6.1) - eslint-compat-utils: 0.5.1(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): dependencies: '@package-json/types': 0.0.12 @@ -21361,45 +16819,6 @@ snapshots: - supports-color optional: true - eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)): - dependencies: - '@package-json/types': 0.0.12 - '@typescript-eslint/types': 8.61.0 - comment-parser: 1.4.7 - debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - eslint-import-context: 0.1.9(unrs-resolver@1.12.2) - is-glob: 4.0.3 - minimatch: 10.2.5 - semver: 7.8.4 - stable-hash-x: 0.2.0 - unrs-resolver: 1.12.2 - optionalDependencies: - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - optional: true - - eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)): - dependencies: - '@package-json/types': 0.0.12 - '@typescript-eslint/types': 8.61.0 - comment-parser: 1.4.7 - debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - eslint-import-context: 0.1.9(unrs-resolver@1.12.2) - is-glob: 4.0.3 - minimatch: 10.2.5 - semver: 7.8.4 - stable-hash-x: 0.2.0 - unrs-resolver: 1.12.2 - optionalDependencies: - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 @@ -21411,36 +16830,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) - hasown: 2.0.2 - is-core-module: 2.16.1 - is-glob: 4.0.3 - minimatch: 3.1.5 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.1 - semver: 6.3.1 - string.prototype.trimend: 1.0.9 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 8.58.0(eslint@8.57.1)(typescript@5.9.3) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): - 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 - doctrine: 2.1.0 - eslint: 9.39.4(jiti@2.6.1) - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -21452,7 +16842,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.58.0(eslint@8.57.1)(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -21477,55 +16867,10 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.4(jiti@2.6.1)): - dependencies: - aria-query: 5.3.2 - array-includes: 3.1.9 - array.prototype.flatmap: 1.3.3 - ast-types-flow: 0.0.8 - axe-core: 4.11.0 - axobject-query: 4.1.0 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 9.39.4(jiti@2.6.1) - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - language-tags: 1.0.9 - minimatch: 3.1.5 - object.fromentries: 2.0.8 - safe-regex-test: 1.1.0 - string.prototype.includes: 2.0.1 - - eslint-plugin-n@17.24.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - enhanced-resolve: 5.20.1 - eslint: 9.39.4(jiti@2.6.1) - eslint-plugin-es-x: 7.8.0(eslint@9.39.4(jiti@2.6.1)) - get-tsconfig: 4.14.0 - globals: 15.15.0 - globrex: 0.1.2 - ignore: 5.3.2 - semver: 7.8.4 - ts-declaration-location: 1.0.7(typescript@6.0.3) - transitivePeerDependencies: - - typescript - eslint-plugin-react-hooks@5.2.0(eslint@8.57.1): dependencies: eslint: 8.57.1 - eslint-plugin-react-hooks@7.1.1(eslint@9.39.4(jiti@2.6.1)): - dependencies: - '@babel/core': 7.29.7 - '@babel/parser': 7.29.7 - eslint: 9.39.4(jiti@2.6.1) - 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@8.57.1): dependencies: array-includes: 3.1.9 @@ -21548,49 +16893,13 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.6.1)): - dependencies: - array-includes: 3.1.9 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.3 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.39.4(jiti@2.6.1) - estraverse: 5.3.0 - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.5 - object.entries: 1.1.9 - object.fromentries: 2.0.8 - object.values: 1.2.1 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.12 - string.prototype.repeat: 1.0.0 - eslint-scope@7.2.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 - eslint-scope@8.4.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-scope@9.1.2: - dependencies: - '@types/esrecurse': 4.3.1 - '@types/estree': 1.0.8 - esrecurse: 4.3.0 - estraverse: 5.3.0 - eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.2.1: {} - eslint-visitor-keys@5.0.1: {} eslint@8.57.1: @@ -21636,59 +16945,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint@9.39.4(jiti@2.6.1): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.2 - '@eslint/config-helpers': 0.4.2 - '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.5 - '@eslint/js': 9.39.4 - '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.8 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.14.0 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.5 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 2.6.1 - transitivePeerDependencies: - - supports-color - - espree@10.4.0: - dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - eslint-visitor-keys: 4.2.1 - - espree@11.2.0: - dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - eslint-visitor-keys: 5.0.1 - espree@9.6.1: dependencies: acorn: 8.15.0 @@ -21783,53 +17039,13 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.1.2 - exit-hook@2.2.1: {} - expect-type@1.2.2: {} - expect-type@1.3.0: {} - express-rate-limit@8.3.2(express@5.2.1): dependencies: express: 5.2.1 ip-address: 10.1.0 - express@4.22.2: - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.5 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.7.1 - cookie-signature: 1.0.7 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.3.2 - fresh: 0.5.2 - http-errors: 2.0.1 - merge-descriptors: 1.0.3 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.13 - proxy-addr: 2.0.7 - qs: 6.15.2 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.19.2 - serve-static: 1.16.3 - setprototypeof: 1.2.0 - statuses: 2.0.2 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - express@5.2.1: dependencies: accepts: 2.0.0 @@ -21950,26 +17166,10 @@ snapshots: dependencies: flat-cache: 3.2.0 - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - finalhandler@1.3.2: - dependencies: - debug: 2.6.9 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - finalhandler@2.1.1: dependencies: debug: 4.4.3 @@ -21998,11 +17198,6 @@ snapshots: keyv: 4.5.4 rimraf: 3.0.2 - flat-cache@4.0.1: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - flatted@3.3.3: {} for-each@0.3.5: @@ -22038,8 +17233,6 @@ snapshots: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - fresh@0.5.2: {} - fresh@2.0.0: {} fs-extra@10.1.0: @@ -22086,7 +17279,7 @@ snapshots: '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@types/react': 19.2.14 lucide-react: 0.522.0(react@19.2.7) - next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) react-router: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -22127,7 +17320,7 @@ snapshots: unist-util-visit: 5.0.0 zod: 4.4.3 optionalDependencies: - next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 vite: 7.3.1(@types/node@24.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) transitivePeerDependencies: @@ -22175,7 +17368,7 @@ snapshots: tailwind-merge: 3.6.0 optionalDependencies: '@types/react': 19.2.14 - next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) tailwindcss: 4.2.2 transitivePeerDependencies: - '@mixedbread/sdk' @@ -22236,8 +17429,6 @@ snapshots: get-port-please@3.2.0: optional: true - get-port@5.1.1: {} - get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -22263,6 +17454,7 @@ snapshots: get-tsconfig@4.14.0: dependencies: resolve-pkg-maps: 1.0.0 + optional: true get-uri@6.0.5: dependencies: @@ -22304,14 +17496,6 @@ snapshots: dependencies: type-fest: 0.20.2 - globals@14.0.0: {} - - globals@15.15.0: {} - - globals@16.4.0: {} - - globals@17.6.0: {} - globalthis@1.0.4: dependencies: define-properties: 1.2.1 @@ -22328,12 +17512,6 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 - globrex@0.1.2: {} - - goober@2.1.19(csstype@3.2.3): - dependencies: - csstype: 3.2.3 - gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -22359,13 +17537,7 @@ snapshots: srvx: 0.11.16 optionalDependencies: crossws: 0.4.6(srvx@0.11.16) - - h3@2.0.1-rc.22(crossws@0.4.6(srvx@0.11.16)): - dependencies: - rou3: 0.8.1 - srvx: 0.11.16 - optionalDependencies: - crossws: 0.4.6(srvx@0.11.16) + optional: true handlebars@4.7.8: dependencies: @@ -22563,12 +17735,6 @@ snapshots: dependencies: '@babel/runtime': 7.29.2 - hermes-estree@0.25.1: {} - - hermes-parser@0.25.1: - dependencies: - hermes-estree: 0.25.1 - highlight.js@10.7.3: {} highlight.js@11.11.1: {} @@ -22579,13 +17745,12 @@ snapshots: hookable@5.5.3: {} - hookable@6.1.1: {} - html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1): dependencies: '@exodus/bytes': 1.15.1(@noble/hashes@2.0.1) transitivePeerDependencies: - '@noble/hashes' + optional: true html-url-attributes@3.0.1: {} @@ -22597,6 +17762,7 @@ snapshots: domhandler: 5.0.3 domutils: 3.2.2 entities: 7.0.1 + optional: true http-errors@2.0.1: dependencies: @@ -22623,8 +17789,6 @@ snapshots: transitivePeerDependencies: - supports-color - httpxy@0.5.3: {} - human-signals@2.1.0: {} human-signals@8.0.1: {} @@ -22636,6 +17800,7 @@ snapshots: iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 + optional: true iconv-lite@0.7.2: dependencies: @@ -22841,7 +18006,8 @@ snapshots: is-plain-obj@4.1.0: {} - is-potential-custom-element-name@1.0.1: {} + is-potential-custom-element-name@1.0.1: + optional: true is-promise@4.0.0: {} @@ -22919,7 +18085,8 @@ snapshots: isbinaryfile@4.0.10: {} - isbot@5.1.42: {} + isbot@5.1.42: + optional: true isexe@2.0.0: {} @@ -22978,8 +18145,7 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' - supports-color - - jsesc@3.0.2: {} + optional: true jsesc@3.1.0: {} @@ -23061,11 +18227,6 @@ snapshots: dependencies: language-subtag-registry: 0.3.23 - launch-editor@2.14.1: - dependencies: - picocolors: 1.1.1 - shell-quote: 1.8.4 - levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -23193,7 +18354,8 @@ snapshots: lru-cache@11.2.7: {} - lru-cache@11.5.1: {} + lru-cache@11.5.1: + optional: true lru-cache@5.1.1: dependencies: @@ -23212,16 +18374,10 @@ snapshots: dependencies: react: 19.2.7 - lucide-react@0.545.0(react@19.2.7): - dependencies: - react: 19.2.7 - lucide-react@1.7.0(react@19.2.7): dependencies: react: 19.2.7 - lz-string@1.5.0: {} - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -23432,23 +18588,17 @@ snapshots: mdurl@2.0.0: {} - media-typer@0.3.0: {} - media-typer@1.1.0: {} memory-pager@1.5.0: optional: true - merge-descriptors@1.0.3: {} - merge-descriptors@2.0.0: {} merge-stream@2.0.0: {} merge2@1.4.1: {} - methods@1.1.2: {} - micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.2.0 @@ -23728,20 +18878,12 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 - mime-db@1.52.0: {} - mime-db@1.54.0: {} - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - mime-types@3.0.2: dependencies: mime-db: 1.54.0 - mime@1.6.0: {} - mimic-fn@2.1.0: {} mimic-function@5.0.1: {} @@ -23789,94 +18931,30 @@ snapshots: pathe: 2.0.3 pkg-types: 1.3.1 ufo: 1.6.1 - - mongodb-connection-string-url@3.0.2: - dependencies: - '@types/whatwg-url': 11.0.5 - whatwg-url: 14.2.0 - optional: true - - mongodb@6.21.0(socks@2.8.7): - dependencies: - '@mongodb-js/saslprep': 1.4.11 - bson: 6.10.4 - mongodb-connection-string-url: 3.0.2 - optionalDependencies: - socks: 2.8.7 - optional: true - - morgan@1.11.0: - dependencies: - basic-auth: 2.0.1 - debug: 2.6.9 - depd: 2.0.0 - on-finished: 2.4.1 - on-headers: 1.1.0 - transitivePeerDependencies: - - supports-color - - motion-dom@12.23.23: - dependencies: - motion-utils: 12.23.6 - - motion-utils@12.23.6: {} - - ms@2.0.0: {} - - ms@2.1.3: {} - - msw@2.12.10(@types/node@22.19.20)(typescript@5.9.3): - dependencies: - '@inquirer/confirm': 5.1.21(@types/node@22.19.20) - '@mswjs/interceptors': 0.41.3 - '@open-draft/deferred-promise': 2.2.0 - '@types/statuses': 2.0.6 - cookie: 1.1.1 - graphql: 16.13.1 - headers-polyfill: 4.0.3 - is-node-process: 1.2.0 - outvariant: 1.4.3 - path-to-regexp: 6.3.0 - picocolors: 1.1.1 - rettime: 0.10.1 - statuses: 2.0.2 - strict-event-emitter: 0.5.1 - tough-cookie: 6.0.1 - type-fest: 5.4.4 - until-async: 3.0.2 - yargs: 17.7.2 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - '@types/node' - optional: true - - msw@2.12.10(@types/node@22.19.20)(typescript@6.0.3): - dependencies: - '@inquirer/confirm': 5.1.21(@types/node@22.19.20) - '@mswjs/interceptors': 0.41.3 - '@open-draft/deferred-promise': 2.2.0 - '@types/statuses': 2.0.6 - cookie: 1.1.1 - graphql: 16.13.1 - headers-polyfill: 4.0.3 - is-node-process: 1.2.0 - outvariant: 1.4.3 - path-to-regexp: 6.3.0 - picocolors: 1.1.1 - rettime: 0.10.1 - statuses: 2.0.2 - strict-event-emitter: 0.5.1 - tough-cookie: 6.0.1 - type-fest: 5.4.4 - until-async: 3.0.2 - yargs: 17.7.2 + + mongodb-connection-string-url@3.0.2: + dependencies: + '@types/whatwg-url': 11.0.5 + whatwg-url: 14.2.0 + optional: true + + mongodb@6.21.0(socks@2.8.7): + dependencies: + '@mongodb-js/saslprep': 1.4.11 + bson: 6.10.4 + mongodb-connection-string-url: 3.0.2 optionalDependencies: - typescript: 6.0.3 - transitivePeerDependencies: - - '@types/node' + socks: 2.8.7 optional: true + motion-dom@12.23.23: + dependencies: + motion-utils: 12.23.6 + + motion-utils@12.23.6: {} + + ms@2.1.3: {} + msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3): dependencies: '@inquirer/confirm': 5.1.21(@types/node@24.12.0) @@ -23960,10 +19038,6 @@ snapshots: natural-compare@1.4.0: {} - negotiator@0.6.3: {} - - negotiator@0.6.4: {} - negotiator@1.0.0: {} neo-async@2.6.2: {} @@ -23975,7 +19049,7 @@ snapshots: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - next@16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + next@16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: '@next/env': 16.0.10 '@swc/helpers': 0.5.15 @@ -23983,7 +19057,7 @@ snapshots: postcss: 8.4.31 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.7) + styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.7) optionalDependencies: '@next/swc-darwin-arm64': 16.0.10 '@next/swc-darwin-x64': 16.0.10 @@ -24027,61 +19101,7 @@ snapshots: transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - - nf3@0.3.17: {} - - nitro@3.0.260603-beta(@electric-sql/pglite@0.3.15)(@vercel/blob@0.27.3)(chokidar@4.0.3)(dotenv@17.2.3)(giget@2.0.0)(jiti@2.6.1)(lru-cache@11.5.1)(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(rollup@4.53.2)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): - dependencies: - consola: 3.4.2 - crossws: 0.4.6(srvx@0.11.16) - db0: 0.3.4(@electric-sql/pglite@0.3.15)(mysql2@3.15.3) - env-runner: 0.1.12 - h3: 2.0.1-rc.22(crossws@0.4.6(srvx@0.11.16)) - hookable: 6.1.1 - nf3: 0.3.17 - ocache: 0.1.5 - ofetch: 2.0.0-alpha.3 - ohash: 2.0.11 - rolldown: 1.1.0 - srvx: 0.11.16 - unenv: 2.0.0-rc.24 - unstorage: 2.0.0-alpha.7(@vercel/blob@0.27.3)(chokidar@4.0.3)(db0@0.3.4(@electric-sql/pglite@0.3.15)(mysql2@3.15.3))(lru-cache@11.5.1)(mongodb@6.21.0(socks@2.8.7))(ofetch@2.0.0-alpha.3) - optionalDependencies: - dotenv: 17.2.3 - giget: 2.0.0 - jiti: 2.6.1 - rollup: 4.53.2 - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@electric-sql/pglite' - - '@libsql/client' - - '@netlify/blobs' - - '@netlify/runtime' - - '@planetscale/database' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - better-sqlite3 - - chokidar - - drizzle-orm - - idb-keyval - - ioredis - - lru-cache - - miniflare - - mongodb - - mysql2 - - sqlite3 - - uploadthing + optional: true no-case@2.3.2: dependencies: @@ -24134,13 +19154,13 @@ snapshots: dependencies: boolbase: 1.0.0 - nuqs@2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7): + nuqs@2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7): dependencies: '@standard-schema/spec': 1.0.0 react: 19.2.7 optionalDependencies: '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react-router: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) nypm@0.6.2: @@ -24197,22 +19217,12 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 - obug@2.1.2: {} - - ocache@0.1.5: - dependencies: - ohash: 2.0.11 - - ofetch@2.0.0-alpha.3: {} - ohash@2.0.11: {} on-finished@2.4.1: dependencies: ee-first: 1.1.1 - on-headers@1.1.0: {} - once@1.4.0: dependencies: wrappy: 1.0.2 @@ -24304,34 +19314,6 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 - oxc-parser@0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): - dependencies: - '@oxc-project/types': 0.120.0 - optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.120.0 - '@oxc-parser/binding-android-arm64': 0.120.0 - '@oxc-parser/binding-darwin-arm64': 0.120.0 - '@oxc-parser/binding-darwin-x64': 0.120.0 - '@oxc-parser/binding-freebsd-x64': 0.120.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.120.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.120.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.120.0 - '@oxc-parser/binding-linux-arm64-musl': 0.120.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.120.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.120.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.120.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.120.0 - '@oxc-parser/binding-linux-x64-gnu': 0.120.0 - '@oxc-parser/binding-linux-x64-musl': 0.120.0 - '@oxc-parser/binding-openharmony-arm64': 0.120.0 - '@oxc-parser/binding-wasm32-wasi': 0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - '@oxc-parser/binding-win32-arm64-msvc': 0.120.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.120.0 - '@oxc-parser/binding-win32-x64-msvc': 0.120.0 - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): optionalDependencies: '@oxc-resolver/binding-android-arm-eabi': 11.19.1 @@ -24391,8 +19373,6 @@ snapshots: dependencies: aggregate-error: 3.1.0 - p-map@7.0.4: {} - pac-proxy-agent@7.2.0: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 @@ -24444,10 +19424,12 @@ snapshots: dependencies: domhandler: 5.0.3 parse5: 7.3.0 + optional: true parse5-parser-stream@7.1.2: dependencies: parse5: 7.3.0 + optional: true parse5@7.3.0: dependencies: @@ -24456,6 +19438,7 @@ snapshots: parse5@8.0.1: dependencies: entities: 8.0.0 + optional: true parseurl@1.3.3: {} @@ -24482,16 +19465,12 @@ snapshots: path-parse@1.0.7: {} - path-to-regexp@0.1.13: {} - path-to-regexp@6.3.0: {} path-to-regexp@8.3.0: {} path-type@4.0.0: {} - pathe@1.1.2: {} - pathe@2.0.3: {} pathval@2.0.1: {} @@ -24532,6 +19511,7 @@ snapshots: confbox: 0.2.4 exsolve: 1.0.8 pathe: 2.0.3 + optional: true playwright-core@1.56.1: {} @@ -24761,24 +19741,11 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-tailwindcss@0.7.4(prettier@3.8.4): - dependencies: - prettier: 3.8.4 - - prettier-plugin-tailwindcss@0.8.0(prettier@3.8.4): - dependencies: - prettier: 3.8.4 - - prettier@3.8.4: {} + prettier@3.8.4: + optional: true pretty-bytes@7.1.0: {} - pretty-format@27.5.1: - dependencies: - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 17.0.2 - pretty-hrtime@1.0.3: {} pretty-ms@9.3.0: @@ -24802,23 +19769,6 @@ snapshots: - react-dom optional: true - prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3): - dependencies: - '@prisma/config': 7.5.0 - '@prisma/dev': 0.20.0(typescript@6.0.3) - '@prisma/engines': 7.5.0 - '@prisma/studio-core': 0.21.1(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - mysql2: 3.15.3 - postgres: 3.4.7 - optionalDependencies: - typescript: 6.0.3 - transitivePeerDependencies: - - '@types/react' - - magicast - - react - - react-dom - optional: true - prismjs@1.30.0: {} prompts@2.4.2: @@ -24995,10 +19945,6 @@ snapshots: dependencies: side-channel: 1.1.0 - qs@6.15.2: - dependencies: - side-channel: 1.1.0 - queue-microtask@1.2.3: {} radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): @@ -25064,78 +20010,8 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - radix-ui@1.5.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-accessible-icon': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-accordion': 1.2.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-alert-dialog': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-arrow': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-aspect-ratio': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-avatar': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-checkbox': 1.3.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-collapsible': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-context-menu': 2.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-dialog': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-dropdown-menu': 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-form': 0.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-hover-card': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-label': 2.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-menu': 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-menubar': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-navigation-menu': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-one-time-password-field': 0.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-password-toggle-field': 0.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-popover': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-progress': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-radio-group': 1.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-scroll-area': 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-select': 2.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-separator': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slider': 1.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-switch': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-tabs': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toast': 1.2.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toggle': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toggle-group': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toolbar': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-tooltip': 1.2.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) - '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - range-parser@1.2.1: {} - raw-body@2.5.3: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - raw-body@3.0.2: dependencies: bytes: 3.1.2 @@ -25187,8 +20063,6 @@ snapshots: react-is@16.13.1: {} - react-is@17.0.2: {} - react-markdown@9.1.0(@types/react@19.2.14)(react@19.2.7): dependencies: '@types/hast': 3.0.4 @@ -25212,10 +20086,6 @@ snapshots: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - react-refresh@0.14.2: {} - - react-refresh@0.18.0: {} - react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.7): dependencies: react: 19.2.7 @@ -25235,17 +20105,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - react-remove-scroll@2.7.2(@types/react@19.2.14)(react@19.2.7): - dependencies: - react: 19.2.7 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.14)(react@19.2.7) - react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.7) - tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.14)(react@19.2.7) - use-sidecar: 1.1.3(@types/react@19.2.14)(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.14 - react-resizable-panels@2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: react: 19.2.7 @@ -25258,6 +20117,7 @@ snapshots: set-cookie-parser: 2.7.2 optionalDependencies: react-dom: 19.2.7(react@19.2.7) + optional: true react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.2.7): dependencies: @@ -25553,6 +20413,7 @@ snapshots: '@rolldown/binding-wasm32-wasi': 1.1.0 '@rolldown/binding-win32-arm64-msvc': 1.1.0 '@rolldown/binding-win32-x64-msvc': 1.1.0 + optional: true rollup-plugin-dts@6.2.3(rollup@4.53.2)(typescript@5.9.3): dependencies: @@ -25615,7 +20476,8 @@ snapshots: rou3@0.7.12: {} - rou3@0.8.1: {} + rou3@0.8.1: + optional: true router@2.2.0: dependencies: @@ -25651,8 +20513,6 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 - safe-buffer@5.1.2: {} - safe-buffer@5.2.1: {} safe-push-apply@1.0.0: @@ -25673,6 +20533,7 @@ snapshots: saxes@6.0.0: dependencies: xmlchars: 2.2.0 + optional: true scheduler@0.27.0: {} @@ -25688,25 +20549,8 @@ snapshots: semver@7.7.3: {} - semver@7.8.4: {} - - send@0.19.2: - dependencies: - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 0.5.2 - http-errors: 2.0.1 - mime: 1.6.0 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color + semver@7.8.4: + optional: true send@1.2.1: dependencies: @@ -25735,17 +20579,10 @@ snapshots: seroval-plugins@1.5.4(seroval@1.5.4): dependencies: seroval: 1.5.4 + optional: true - seroval@1.5.4: {} - - serve-static@1.16.3: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 0.19.2 - transitivePeerDependencies: - - supports-color + seroval@1.5.4: + optional: true serve-static@2.2.1: dependencies: @@ -25753,133 +20590,50 @@ snapshots: escape-html: 1.0.3 parseurl: 1.3.3 send: 1.2.1 - transitivePeerDependencies: - - supports-color - - set-cookie-parser@2.7.2: {} - - set-cookie-parser@3.1.0: {} - - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - - set-proto@1.0.0: - dependencies: - dunder-proto: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - - setprototypeof@1.2.0: {} - - shadcn@4.1.2(@types/node@24.12.0)(typescript@5.9.3): - dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.2 - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.0) - '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@dotenvx/dotenvx': 1.59.1 - '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) - '@types/validate-npm-package-name': 4.0.2 - browserslist: 4.28.0 - commander: 14.0.3 - cosmiconfig: 9.0.1(typescript@5.9.3) - dedent: 1.7.0 - deepmerge: 4.3.1 - diff: 8.0.4 - execa: 9.6.1 - fast-glob: 3.3.3 - fs-extra: 11.3.2 - fuzzysort: 3.1.0 - https-proxy-agent: 7.0.6 - kleur: 4.1.5 - msw: 2.12.10(@types/node@24.12.0)(typescript@5.9.3) - node-fetch: 3.3.2 - open: 11.0.0 - ora: 8.2.0 - postcss: 8.5.6 - postcss-selector-parser: 7.1.0 - prompts: 2.4.2 - recast: 0.23.11 - stringify-object: 5.0.0 - tailwind-merge: 3.6.0 - ts-morph: 26.0.0 - tsconfig-paths: 4.2.0 - validate-npm-package-name: 7.0.2 - zod: 4.4.3 - zod-to-json-schema: 3.25.2(zod@4.4.3) - transitivePeerDependencies: - - '@cfworker/json-schema' - - '@types/node' - - babel-plugin-macros - - supports-color - - typescript - - shadcn@4.11.0(typescript@5.9.3): - dependencies: - '@babel/core': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.7) - '@babel/preset-typescript': 7.28.5(@babel/core@7.29.7) - '@dotenvx/dotenvx': 1.59.1 - '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) - '@types/validate-npm-package-name': 4.0.2 - browserslist: 4.28.2 - commander: 14.0.3 - cosmiconfig: 9.0.1(typescript@5.9.3) - dedent: 1.7.0 - deepmerge: 4.3.1 - diff: 8.0.4 - execa: 9.6.1 - fast-glob: 3.3.3 - fs-extra: 11.3.2 - fuzzysort: 3.1.0 - https-proxy-agent: 7.0.6 - kleur: 4.1.5 - node-fetch: 3.3.2 - open: 11.0.0 - ora: 8.2.0 - postcss: 8.5.6 - postcss-selector-parser: 7.1.0 - prompts: 2.4.2 - recast: 0.23.11 - stringify-object: 5.0.0 - tailwind-merge: 3.6.0 - ts-morph: 26.0.0 - tsconfig-paths: 4.2.0 - validate-npm-package-name: 7.0.2 - zod: 4.4.3 - zod-to-json-schema: 3.25.2(zod@4.4.3) - transitivePeerDependencies: - - '@cfworker/json-schema' - - babel-plugin-macros + transitivePeerDependencies: - supports-color - - typescript - shadcn@4.11.0(typescript@6.0.3): + set-cookie-parser@2.7.2: + optional: true + + set-cookie-parser@3.1.0: {} + + set-function-length@1.2.2: dependencies: - '@babel/core': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.7) - '@babel/preset-typescript': 7.28.5(@babel/core@7.29.7) + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setprototypeof@1.2.0: {} + + shadcn@4.1.2(@types/node@24.12.0)(typescript@5.9.3): + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.2 + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) '@dotenvx/dotenvx': 1.59.1 '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) '@types/validate-npm-package-name': 4.0.2 - browserslist: 4.28.2 + browserslist: 4.28.0 commander: 14.0.3 - cosmiconfig: 9.0.1(typescript@6.0.3) + cosmiconfig: 9.0.1(typescript@5.9.3) dedent: 1.7.0 deepmerge: 4.3.1 diff: 8.0.4 @@ -25889,6 +20643,7 @@ snapshots: fuzzysort: 3.1.0 https-proxy-agent: 7.0.6 kleur: 4.1.5 + msw: 2.12.10(@types/node@24.12.0)(typescript@5.9.3) node-fetch: 3.3.2 open: 11.0.0 ora: 8.2.0 @@ -25905,6 +20660,7 @@ snapshots: zod-to-json-schema: 3.25.2(zod@4.4.3) transitivePeerDependencies: - '@cfworker/json-schema' + - '@types/node' - babel-plugin-macros - supports-color - typescript @@ -25947,8 +20703,6 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.4: {} - shiki@3.15.0: dependencies: '@shikijs/core': 3.15.0 @@ -26028,6 +20782,7 @@ snapshots: csstype: 3.2.3 seroval: 1.5.4 seroval-plugins: 1.5.4(seroval@1.5.4) + optional: true sonner@2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: @@ -26036,11 +20791,6 @@ snapshots: source-map-js@1.2.1: {} - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - source-map@0.6.1: {} source-map@0.7.6: {} @@ -26055,9 +20805,11 @@ snapshots: sqlstring@2.3.3: optional: true - srvx@0.11.16: {} + srvx@0.11.16: + optional: true - stable-hash-x@0.2.0: {} + stable-hash-x@0.2.0: + optional: true stable-hash@0.0.5: {} @@ -26067,8 +20819,6 @@ snapshots: std-env@3.10.0: {} - std-env@4.1.0: {} - stdin-discarder@0.2.2: {} stop-iteration-iterator@1.1.0: @@ -26191,12 +20941,20 @@ snapshots: dependencies: inline-style-parser: 0.2.7 + styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.7): + dependencies: + client-only: 0.0.1 + react: 19.2.7 + optionalDependencies: + '@babel/core': 7.29.0 + styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.7): dependencies: client-only: 0.0.1 react: 19.2.7 optionalDependencies: '@babel/core': 7.29.7 + optional: true stylehacks@7.0.7(postcss@8.5.6): dependencies: @@ -26235,7 +20993,8 @@ snapshots: react: 19.2.7 use-sync-external-store: 1.6.0(react@19.2.7) - symbol-tree@3.2.4: {} + symbol-tree@3.2.4: + optional: true tabbable@6.4.0: {} @@ -26247,12 +21006,8 @@ snapshots: tailwindcss@4.2.2: {} - tailwindcss@4.3.0: {} - tapable@2.3.0: {} - tapable@2.3.3: {} - text-table@0.2.0: {} thenby@1.3.4: {} @@ -26290,8 +21045,6 @@ snapshots: tinyrainbow@2.0.0: {} - tinyrainbow@3.1.0: {} - tinyspy@4.0.4: {} title-case@2.1.1: @@ -26327,6 +21080,7 @@ snapshots: tr46@6.0.0: dependencies: punycode: 2.3.1 + optional: true trim-lines@3.0.1: {} @@ -26336,15 +21090,6 @@ snapshots: dependencies: typescript: 5.9.3 - ts-api-utils@2.5.0(typescript@6.0.3): - dependencies: - typescript: 6.0.3 - - ts-declaration-location@1.0.7(typescript@6.0.3): - dependencies: - picomatch: 4.0.4 - typescript: 6.0.3 - ts-morph@26.0.0: dependencies: '@ts-morph/common': 0.27.0 @@ -26373,14 +21118,6 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - tsconfck@3.1.6(typescript@5.9.3): - optionalDependencies: - typescript: 5.9.3 - - tsconfck@3.1.6(typescript@6.0.3): - optionalDependencies: - typescript: 6.0.3 - tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -26410,6 +21147,7 @@ snapshots: esbuild: 0.28.0 optionalDependencies: fsevents: 2.3.3 + optional: true turbo-darwin-64@2.6.1: optional: true @@ -26452,11 +21190,6 @@ snapshots: dependencies: tagged-tag: 1.0.0 - type-is@1.6.18: - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 - type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -26496,37 +21229,14 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): - dependencies: - '@typescript-eslint/eslint-plugin': 8.61.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.61.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.4(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - typescript-eslint@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3): - dependencies: - '@typescript-eslint/eslint-plugin': 8.61.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/parser': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - eslint: 9.39.4(jiti@2.6.1) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - typescript@5.9.3: {} - typescript@6.0.3: {} - uc.micro@2.1.0: {} ufo@1.6.1: {} - ufo@1.6.4: {} + ufo@1.6.4: + optional: true uglify-js@3.19.3: optional: true @@ -26586,11 +21296,8 @@ snapshots: dependencies: '@fastify/busboy': 2.1.1 - undici@7.27.2: {} - - unenv@2.0.0-rc.24: - dependencies: - pathe: 2.0.3 + undici@7.27.2: + optional: true unicorn-magic@0.3.0: {} @@ -26651,6 +21358,7 @@ snapshots: acorn: 8.16.0 picomatch: 4.0.4 webpack-virtual-modules: 0.6.2 + optional: true unrs-resolver@1.11.1: dependencies: @@ -26702,15 +21410,7 @@ snapshots: '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2 '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 - - unstorage@2.0.0-alpha.7(@vercel/blob@0.27.3)(chokidar@4.0.3)(db0@0.3.4(@electric-sql/pglite@0.3.15)(mysql2@3.15.3))(lru-cache@11.5.1)(mongodb@6.21.0(socks@2.8.7))(ofetch@2.0.0-alpha.3): - optionalDependencies: - '@vercel/blob': 0.27.3 - chokidar: 4.0.3 - db0: 0.3.4(@electric-sql/pglite@0.3.15)(mysql2@3.15.3) - lru-cache: 11.5.1 - mongodb: 6.21.0(socks@2.8.7) - ofetch: 2.0.0-alpha.3 + optional: true until-async@3.0.2: {} @@ -26774,17 +21474,11 @@ snapshots: util-deprecate@1.0.2: {} - utils-merge@1.0.1: {} - v8-compile-cache-lib@3.0.1: {} valibot@1.2.0(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 - - valibot@1.2.0(typescript@6.0.3): - optionalDependencies: - typescript: 6.0.3 optional: true validate-npm-package-name@5.0.1: {} @@ -26817,27 +21511,6 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-node@3.2.4(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - vite-node@3.2.4(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 @@ -26901,44 +21574,6 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): - dependencies: - debug: 4.4.3 - globrex: 0.1.2 - tsconfck: 3.1.6(typescript@5.9.3) - optionalDependencies: - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color - - typescript - - vite-tsconfig-paths@5.1.4(typescript@6.0.3)(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): - dependencies: - debug: 4.4.3 - globrex: 0.1.2 - tsconfck: 3.1.6(typescript@6.0.3) - optionalDependencies: - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color - - typescript - - vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.6 - rollup: 4.53.2 - tinyglobby: 0.2.17 - optionalDependencies: - '@types/node': 22.19.20 - fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.32.0 - tsx: 4.22.4 - yaml: 2.8.2 - vite@7.3.1(@types/node@24.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2): dependencies: esbuild: 0.27.3 @@ -27004,37 +21639,11 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 - vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.6 - rollup: 4.53.2 - tinyglobby: 0.2.17 - optionalDependencies: - '@types/node': 25.5.0 - fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.32.0 - tsx: 4.22.4 - yaml: 2.8.2 - optional: true - - vitefu@1.1.3(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): - optionalDependencies: - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - vitefu@1.1.3(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)): optionalDependencies: vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) optional: true - vitefu@1.1.3(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): - optionalDependencies: - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - optional: true - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 @@ -27164,107 +21773,6 @@ snapshots: - tsx - yaml - vitest@4.1.8(@opentelemetry/api@1.9.0)(@types/node@22.19.20)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@22.19.20)(typescript@5.9.3))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): - dependencies: - '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(msw@2.12.10(@types/node@22.19.20)(typescript@5.9.3))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.8 - '@vitest/runner': 4.1.8 - '@vitest/snapshot': 4.1.8 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.2 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@types/node': 22.19.20 - jsdom: 28.1.0(@noble/hashes@2.0.1) - transitivePeerDependencies: - - msw - optional: true - - vitest@4.1.8(@opentelemetry/api@1.9.0)(@types/node@22.19.20)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@22.19.20)(typescript@6.0.3))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): - dependencies: - '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(msw@2.12.10(@types/node@22.19.20)(typescript@6.0.3))(vite@7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.8 - '@vitest/runner': 4.1.8 - '@vitest/snapshot': 4.1.8 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.2 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@22.19.20)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@types/node': 22.19.20 - jsdom: 28.1.0(@noble/hashes@2.0.1) - transitivePeerDependencies: - - msw - - vitest@4.1.8(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): - dependencies: - '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.8 - '@vitest/runner': 4.1.8 - '@vitest/snapshot': 4.1.8 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.2 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@types/node': 25.5.0 - jsdom: 28.1.0(@noble/hashes@2.0.1) - transitivePeerDependencies: - - msw - optional: true - - vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@2.6.1)): - dependencies: - debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - eslint-scope: 9.1.2 - eslint-visitor-keys: 5.0.1 - espree: 11.2.0 - esquery: 1.6.0 - semver: 7.8.4 - transitivePeerDependencies: - - supports-color - vue@3.5.24(typescript@5.9.3): dependencies: '@vue/compiler-dom': 3.5.24 @@ -27275,22 +21783,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - vue@3.5.24(typescript@6.0.3): - dependencies: - '@vue/compiler-dom': 3.5.24 - '@vue/compiler-sfc': 3.5.24 - '@vue/runtime-dom': 3.5.24 - '@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@6.0.3)) - '@vue/shared': 3.5.24 - optionalDependencies: - typescript: 6.0.3 - optional: true - w3c-keyname@2.2.8: {} w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 + optional: true walk-up-path@4.0.0: {} @@ -27305,17 +21803,22 @@ snapshots: webidl-conversions@7.0.0: optional: true - webidl-conversions@8.0.1: {} + webidl-conversions@8.0.1: + optional: true - webpack-virtual-modules@0.6.2: {} + webpack-virtual-modules@0.6.2: + optional: true whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 + optional: true - whatwg-mimetype@4.0.0: {} + whatwg-mimetype@4.0.0: + optional: true - whatwg-mimetype@5.0.0: {} + whatwg-mimetype@5.0.0: + optional: true whatwg-url@14.2.0: dependencies: @@ -27330,6 +21833,7 @@ snapshots: webidl-conversions: 8.0.1 transitivePeerDependencies: - '@noble/hashes' + optional: true which-boxed-primitive@1.1.1: dependencies: @@ -27403,14 +21907,13 @@ snapshots: wrappy@1.0.2: {} - ws@8.21.0: {} - wsl-utils@0.3.1: dependencies: is-wsl: 3.1.1 powershell-utils: 0.1.0 - xml-name-validator@5.0.0: {} + xml-name-validator@5.0.0: + optional: true xmlbuilder2@4.0.3: dependencies: @@ -27418,8 +21921,10 @@ snapshots: '@oozcitak/infra': 2.0.2 '@oozcitak/util': 10.0.0 js-yaml: 4.2.0 + optional: true - xmlchars@2.2.0: {} + xmlchars@2.2.0: + optional: true y18n@5.0.8: {} @@ -27457,11 +21962,8 @@ snapshots: dependencies: zod: 4.4.3 - zod-validation-error@4.0.2(zod@4.4.3): - dependencies: - zod: 4.4.3 - - zod@3.25.76: {} + zod@3.25.76: + optional: true zod@4.4.3: {} diff --git a/scripts/codegen/files/nextjs/lib/plugins/todo/client/client.tsx b/scripts/codegen/files/nextjs/lib/plugins/todo/client/client.tsx index 0fa8f8ead..061bb2ef3 100644 --- a/scripts/codegen/files/nextjs/lib/plugins/todo/client/client.tsx +++ b/scripts/codegen/files/nextjs/lib/plugins/todo/client/client.tsx @@ -1,13 +1,23 @@ import { createApiClient, defineClientPlugin, - createRoute, + defineRoute, } from "@btst/stack/plugins/client"; import type { QueryClient } from "@tanstack/react-query"; import type { TodosApiRouter } from "../api/backend"; import { lazy } from "react"; import type { Todo } from "../types"; +// Stable lazy references at module scope — must NOT be created inside route +// handlers or component bodies, otherwise React sees a new component type on +// every render and cannot hydrate the SSR-rendered HTML. +const TodosListPage = lazy(() => + import("./components").then((m) => ({ default: m.TodosListPage })), +); +const AddTodoPage = lazy(() => + import("./components").then((m) => ({ default: m.AddTodoPage })), +); + /** * Configuration for todos client plugin * Note: queryClient is passed at runtime to both loader and meta (for SSR isolation) @@ -122,26 +132,14 @@ export const todosClientPlugin = (config: TodosClientConfig) => name: "todos", routes: () => ({ - todos: createRoute("/todos", () => { - const TodosListPage = lazy(() => - import("./components").then((m) => ({ default: m.TodosListPage })), - ); - - return { - PageComponent: TodosListPage, - loader: todosLoader(config), - meta: createTodosMeta(config, "/todos"), - }; + todos: defineRoute("/todos", { + page: TodosListPage, + loader: todosLoader(config), + meta: createTodosMeta(config, "/todos"), }), - addTodo: createRoute("/todos/add", () => { - const AddTodoPage = lazy(() => - import("./components").then((m) => ({ default: m.AddTodoPage })), - ); - - return { - PageComponent: AddTodoPage, - meta: createAddTodoMeta(config, "/todos/add"), - }; + addTodo: defineRoute("/todos/add", { + page: AddTodoPage, + meta: createAddTodoMeta(config, "/todos/add"), }), }), sitemap: async () => { diff --git a/scripts/codegen/files/react-router/app/lib/plugins/todo/client/client.tsx b/scripts/codegen/files/react-router/app/lib/plugins/todo/client/client.tsx index 0591c1431..5c9edd566 100644 --- a/scripts/codegen/files/react-router/app/lib/plugins/todo/client/client.tsx +++ b/scripts/codegen/files/react-router/app/lib/plugins/todo/client/client.tsx @@ -1,7 +1,7 @@ import { createApiClient, defineClientPlugin, - createRoute, + defineRoute, } from "@btst/stack/plugins/client"; import type { QueryClient } from "@tanstack/react-query"; import type { TodosApiRouter } from "../api/backend"; @@ -132,15 +132,15 @@ export const todosClientPlugin = (config: TodosClientConfig) => name: "todos", routes: () => ({ - todos: createRoute("/todos", () => ({ - PageComponent: TodosListPageLazy, + todos: defineRoute("/todos", { + page: TodosListPageLazy, loader: todosLoader(config), meta: createTodosMeta(config, "/todos"), - })), - addTodo: createRoute("/todos/add", () => ({ - PageComponent: AddTodoPageLazy, + }), + addTodo: defineRoute("/todos/add", { + page: AddTodoPageLazy, meta: createAddTodoMeta(config, "/todos/add"), - })), + }), }), sitemap: async () => { return [ diff --git a/scripts/codegen/files/tanstack/src/lib/plugins/todo/client/client.tsx b/scripts/codegen/files/tanstack/src/lib/plugins/todo/client/client.tsx index 0591c1431..5c9edd566 100644 --- a/scripts/codegen/files/tanstack/src/lib/plugins/todo/client/client.tsx +++ b/scripts/codegen/files/tanstack/src/lib/plugins/todo/client/client.tsx @@ -1,7 +1,7 @@ import { createApiClient, defineClientPlugin, - createRoute, + defineRoute, } from "@btst/stack/plugins/client"; import type { QueryClient } from "@tanstack/react-query"; import type { TodosApiRouter } from "../api/backend"; @@ -132,15 +132,15 @@ export const todosClientPlugin = (config: TodosClientConfig) => name: "todos", routes: () => ({ - todos: createRoute("/todos", () => ({ - PageComponent: TodosListPageLazy, + todos: defineRoute("/todos", { + page: TodosListPageLazy, loader: todosLoader(config), meta: createTodosMeta(config, "/todos"), - })), - addTodo: createRoute("/todos/add", () => ({ - PageComponent: AddTodoPageLazy, + }), + addTodo: defineRoute("/todos/add", { + page: AddTodoPageLazy, meta: createAddTodoMeta(config, "/todos/add"), - })), + }), }), sitemap: async () => { return [ From f9a071e9c5dbc7f27797ae764e214164bbf3a83c Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Fri, 3 Jul 2026 16:25:23 -0400 Subject: [PATCH 002/380] chore: add Arch/Podman devcontainer configuration Co-authored-by: Cursor --- .devcontainer/Dockerfile | 71 ++++++++++++++++ .devcontainer/linux-podman/devcontainer.json | 89 ++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/linux-podman/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..574523242 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,71 @@ +FROM archlinux:latest + +# Sync package database and install base tooling +# docker CLI (no daemon) lets `docker compose` talk to the host Podman socket +RUN pacman -Syu --noconfirm \ + && pacman -S --noconfirm --needed \ + base-devel \ + bash \ + ca-certificates \ + curl \ + docker \ + docker-compose \ + git \ + github-cli \ + make \ + openssh \ + procps-ng \ + sudo \ + unzip \ + && pacman -Scc --noconfirm + +# Chromium runtime libraries for Playwright e2e tests. `playwright install-deps` +# only targets Debian/Ubuntu, so on Arch we install the shared libs Chromium +# links against ourselves (derived from `ldd chrome | grep "not found"`). +RUN pacman -Sy --noconfirm --needed \ + alsa-lib \ + at-spi2-core \ + atk \ + cairo \ + cups \ + libdrm \ + libx11 \ + libxcb \ + libxcomposite \ + libxdamage \ + libxext \ + libxfixes \ + libxkbcommon \ + libxrandr \ + mesa \ + nspr \ + nss \ + pango \ + && pacman -Scc --noconfirm + +# Create a non-root user matching the host UID/GID (1000/1000 on SteamOS) +ARG USERNAME=dev +ARG USER_UID=1000 +ARG USER_GID=1000 +RUN groupadd --gid $USER_GID $USERNAME \ + && useradd --uid $USER_UID --gid $USER_GID -m -s /bin/bash $USERNAME \ + && echo "$USERNAME ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/$USERNAME \ + && chmod 0440 /etc/sudoers.d/$USERNAME + +USER $USERNAME +WORKDIR /home/$USERNAME + +# Install mise (runtime version manager) +RUN curl https://mise.run | sh +ENV PATH="/home/$USERNAME/.local/bin:$PATH" + +# Node 22.18.0 matches the repo's .nvmrc; pnpm 10.17.1 matches the +# "packageManager" field in package.json (activated via corepack). +RUN mise use --global node@22.18.0 \ + && mise exec -- corepack enable \ + && mise exec -- corepack prepare pnpm@10.17.1 --activate + +RUN echo 'eval "$(mise activate bash)"' >> ~/.bashrc \ + && echo 'eval "$(mise activate bash)"' >> ~/.profile + +ENV PATH="/home/$USERNAME/.local/share/mise/shims:$PATH" diff --git a/.devcontainer/linux-podman/devcontainer.json b/.devcontainer/linux-podman/devcontainer.json new file mode 100644 index 000000000..09343061a --- /dev/null +++ b/.devcontainer/linux-podman/devcontainer.json @@ -0,0 +1,89 @@ +{ + // ─── To reuse this in another project ──────────────────────────────────────── + // 1. Copy .devcontainer/ into the new repo + // 2. Update "name" and "postCreateCommand" + // 3. Everything else (Dockerfile, mounts, runArgs) is project-agnostic + // ───────────────────────────────────────────────────────────────────────────── + "name": "better-stack", + "build": { + "dockerfile": "../Dockerfile", + "args": { + "USERNAME": "dev", + "USER_UID": "1000", + "USER_GID": "1000" + } + }, + + // Podman-specific flags only — no volume paths here (those are in "mounts") + // --userns=keep-id: rootless Podman maps your UID into the container so file ownership matches + // --network=host: shares host network stack (localhost = host) + "runArgs": ["--userns=keep-id", "--network=host"], + + // ${localEnv:HOME} → /home/ on any Linux machine + // ${localEnv:XDG_RUNTIME_DIR} → /run/user/, where the Podman socket lives + "mounts": [ + // Podman socket → docker CLI inside the container talks to host Podman (no daemon needed). + // Mounted at the SAME path as on the host so docker-out-of-docker path forwarding works. + { + "source": "${localEnv:XDG_RUNTIME_DIR}/podman/podman.sock", + "target": "${localEnv:XDG_RUNTIME_DIR}/podman/podman.sock", + "type": "bind" + }, + // pnpm content-addressable store → packages survive image rebuilds + { + "source": "${localEnv:HOME}/.local/share/pnpm", + "target": "/home/dev/.local/share/pnpm", + "type": "bind" + }, + // Playwright browser cache → Chromium download survives rebuilds + { + "source": "${localEnv:HOME}/.cache/ms-playwright", + "target": "/home/dev/.cache/ms-playwright", + "type": "bind" + }, + // SSH keys (read-only) → git push/pull over SSH works inside the container + { + "source": "${localEnv:HOME}/.ssh", + "target": "/home/dev/.ssh", + "type": "bind", + "readonly": true + }, + // gh CLI auth token → survives container rebuilds without re-authenticating + { + "source": "${localEnv:HOME}/.config/gh", + "target": "/home/dev/.config/gh", + "type": "bind" + } + ], + + "containerEnv": { + // Point at the socket's real host path (mounted 1:1 above) so any path the + // CLI forwards to sibling containers resolves identically on the host. + "DOCKER_HOST": "unix://${localEnv:XDG_RUNTIME_DIR}/podman/podman.sock" + }, + + // Mount the workspace at its REAL host path (not /workspaces/...) so any + // project-relative paths forwarded to sibling containers resolve on the HOST. + "workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind,consistency=cached", + "workspaceFolder": "${localWorkspaceFolder}", + + // reshim: regenerates mise shims to point to this container's mise binary + // store-dir: pins pnpm store so it never falls back to a project-local .pnpm-store/ + "postCreateCommand": "mise reshim && pnpm config set store-dir /home/dev/.local/share/pnpm/store && pnpm install", + + // NOTE: No "forwardPorts" here on purpose. This container runs with + // --network=host (see runArgs), so it already shares the host's network + // namespace and every dev server (docs site, codegen project on 3006, etc.) + // is directly reachable on the host. + + "customizations": { + "vscode": { + "extensions": ["biomejs.biome", "ms-playwright.playwright"], + "settings": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "biomejs.biome", + "terminal.integrated.defaultProfile.linux": "bash" + } + } + } +} From b259a74a3eff9edb570616068fdbf84e71037597 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Fri, 3 Jul 2026 16:32:06 -0400 Subject: [PATCH 003/380] docs: document props/route-context merge precedence in ComposedRoute Co-authored-by: Cursor --- packages/stack/src/client/components/compose.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/stack/src/client/components/compose.tsx b/packages/stack/src/client/components/compose.tsx index d001f1766..b0a8a86fc 100644 --- a/packages/stack/src/client/components/compose.tsx +++ b/packages/stack/src/client/components/compose.tsx @@ -70,7 +70,10 @@ export function RouteRenderer({ * @param LoadingComponent - Component to show during suspense * @param onNotFound - Optional callback when route is not found * @param NotFoundComponent - Optional component to show for 404s - * @param props - Additional props to pass to the page component + * @param props - Additional props to pass to the page component. For routes + * created with `defineRoute`, these are merged after the route context, so + * a prop named `params` or `query` intentionally takes precedence over the + * router-extracted values. Only pass trusted, framework-controlled values. * @param onError - Error handler callback for the error boundary */ export function ComposedRoute({ From d45325ce1ce6d94b83e68a47bb47df13d0cf9cd4 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Fri, 3 Jul 2026 16:57:13 -0400 Subject: [PATCH 004/380] chore: reduce codegen E2E retries to 1 on CI Co-authored-by: Cursor --- e2e/playwright.codegen.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/playwright.codegen.config.ts b/e2e/playwright.codegen.config.ts index 36d068fe8..6e662394e 100644 --- a/e2e/playwright.codegen.config.ts +++ b/e2e/playwright.codegen.config.ts @@ -160,7 +160,7 @@ export default defineConfig({ expect: { timeout: 30_000, }, - retries: process.env["CI"] ? 2 : 0, + retries: process.env["CI"] ? 1 : 0, use: { trace: "retain-on-failure", video: "retain-on-failure", From 53c6c2ca52fc8a06b623bda08fd81ef3a04badf6 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:17:17 -0400 Subject: [PATCH 005/380] fix: pin Tailwind source() in tanstack codegen for deterministic CSS hashes Without an explicit source(), Tailwind auto-detects candidates per Vite environment, so the SSR build emitted a differently-hashed styles-*.css URL than the client build, causing a 404 on every SSR-rendered page and failing the tanstack codegen E2E suite. Co-authored-by: Cursor --- scripts/codegen/setup-tanstack.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/codegen/setup-tanstack.sh b/scripts/codegen/setup-tanstack.sh index c6079617e..17448887b 100755 --- a/scripts/codegen/setup-tanstack.sh +++ b/scripts/codegen/setup-tanstack.sh @@ -106,6 +106,16 @@ while IFS= read -r -d '' src_file; do done < <(find "$FILES_DIR" -type f -print0) success "$FILE_COUNT files copied" +# Pin Tailwind's content scanning to src/ so the client and SSR builds produce +# byte-identical CSS. Without an explicit source(), Tailwind auto-detects +# candidates per Vite environment, and the SSR build (run by nitro from a +# different root) emits a differently-hashed styles-*.css URL that 404s at +# runtime. See https://github.com/TanStack/router/issues/4959 +step "Pinning Tailwind source() for deterministic client/SSR CSS hashes" +sed -i 's|^@import "tailwindcss";$|@import "tailwindcss" source("./");|' "$DEST/src/styles.css" +grep -q 'tailwindcss" source' "$DEST/src/styles.css" || die "Failed to patch tailwind source() in src/styles.css" +success "Tailwind source() pinned" + # ── Step 6: Patch package.json ──────────────────────────────────────────────── step "Patching package.json" From 3e0f88064470f0cda788b3df6f0bcfae6be8d96d Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Fri, 3 Jul 2026 22:13:39 +0000 Subject: [PATCH 006/380] feat: add top-level router/api props to StackProvider with framework presets (#127) Promotes the repeated per-plugin Link/navigate/refresh/Image and apiBaseURL/apiBasePath wiring to top-level `router` and `api` props on StackProvider, with shipped presets at @btst/stack/next, @btst/stack/react-router, and @btst/stack/tanstack. Resolution order is plugin override -> top-level router/api -> plugin defaults, so existing per-plugin overrides keep working as an escape hatch (non-breaking). The StackRouter contract already includes the getSearchParams/ setSearchParams fields required by #133. Co-authored-by: Cursor --- docs/content/docs/installation.mdx | 63 +- packages/stack/build.config.ts | 11 + packages/stack/knip.json | 8 +- packages/stack/package.json | 55 + .../src/__tests__/router-overrides.test.tsx | 193 + packages/stack/src/context/index.ts | 1 + packages/stack/src/context/provider.tsx | 157 +- packages/stack/src/context/router.tsx | 97 + packages/stack/src/next/index.tsx | 102 + packages/stack/src/react-router/index.tsx | 63 + packages/stack/src/tanstack/index.tsx | 61 + pnpm-lock.yaml | 4757 +++++++++++++++-- .../files/nextjs/app/cms-example/page.tsx | 42 +- .../files/nextjs/app/directory/[id]/page.tsx | 16 +- .../directory/category/[categoryId]/page.tsx | 16 +- .../files/nextjs/app/directory/page.tsx | 15 +- .../codegen/files/nextjs/app/pages/layout.tsx | 239 +- .../react-router/app/routes/cms-example.tsx | 12 +- .../routes/directory/category.$categoryId.tsx | 18 +- .../app/routes/directory/index.tsx | 18 +- .../app/routes/directory/resource.$id.tsx | 18 +- .../react-router/app/routes/pages/_layout.tsx | 176 +- .../files/tanstack/src/routes/cms-example.tsx | 14 +- .../tanstack/src/routes/directory/$id.tsx | 23 +- .../routes/directory/category/$categoryId.tsx | 23 +- .../tanstack/src/routes/directory/index.tsx | 18 +- .../files/tanstack/src/routes/pages/route.tsx | 180 +- 27 files changed, 5229 insertions(+), 1167 deletions(-) create mode 100644 packages/stack/src/__tests__/router-overrides.test.tsx create mode 100644 packages/stack/src/context/router.tsx create mode 100644 packages/stack/src/next/index.tsx create mode 100644 packages/stack/src/react-router/index.tsx create mode 100644 packages/stack/src/tanstack/index.tsx diff --git a/docs/content/docs/installation.mdx b/docs/content/docs/installation.mdx index f80cd665f..e197a4b37 100644 --- a/docs/content/docs/installation.mdx +++ b/docs/content/docs/installation.mdx @@ -644,16 +644,15 @@ In order to use BTST, your application must meet the following requirements: ### Set Up Layout Provider - Wrap your BTST pages with the `StackProvider` to enable framework-specific overrides: + Wrap your BTST pages with the `StackProvider`. The framework router preset (`router` prop) wires `Link`, `Image`, `navigate`, and `refresh` for every plugin at once, and the `api` prop sets `apiBaseURL`/`apiBasePath` for all plugins. The `overrides` prop is then only needed for genuinely plugin-specific values: ```tsx title="app/pages/[[...all]]/layout.tsx" + "use client" import { StackProvider } from "@btst/stack/context" + import { nextRouter } from "@btst/stack/next" import type { ExamplePluginOverrides } from "@btst/stack/plugins/example/client" - import Link from "next/link" - import Image from "next/image" - import { useRouter } from "next/navigation" // Define the shape of all plugin overrides for type safety type PluginOverrides = { @@ -661,18 +660,20 @@ In order to use BTST, your application must meet the following requirements: // Add other plugins here } + const getBaseURL = () => + typeof window !== "undefined" + ? window.location.origin + : process.env.BASE_URL || "http://localhost:3000" + export default function Layout({ children }) { - const router = useRouter() - return ( basePath="/pages" + router={nextRouter()} + api={{ baseURL: getBaseURL(), basePath: "/api/data" }} overrides={{ example: { - Link: (props) => , - Image: (props) => , - navigate: (path) => router.push(path), - // Add other plugin overrides here + // Only plugin-specific overrides needed here } // Add other plugins here }} @@ -686,8 +687,9 @@ In order to use BTST, your application must meet the following requirements: ```tsx title="app/routes/pages/_layout.tsx" - import { Outlet, Link, useNavigate } from "react-router" + import { Outlet } from "react-router" import { StackProvider } from "@btst/stack/context" + import { reactRouter } from "@btst/stack/react-router" import type { ExamplePluginOverrides } from "@btst/stack/plugins/example/client" // Define the shape of all plugin overrides @@ -696,21 +698,20 @@ In order to use BTST, your application must meet the following requirements: // Add other plugins here } + const getBaseURL = () => + typeof window !== "undefined" + ? window.location.origin + : process.env.BASE_URL || "http://localhost:3000" + export default function Layout() { - const navigate = useNavigate() - return ( basePath="/pages" + router={reactRouter()} + api={{ baseURL: getBaseURL(), basePath: "/api/data" }} overrides={{ example: { - navigate: (href) => navigate(href), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ) - // Add other plugin overrides here + // Only plugin-specific overrides needed here } // Add other plugins here }} @@ -725,9 +726,10 @@ In order to use BTST, your application must meet the following requirements: ```tsx title="src/routes/pages/route.tsx" import { StackProvider } from "@btst/stack/context" + import { tanstackRouter } from "@btst/stack/tanstack" import { QueryClientProvider } from "@tanstack/react-query" import type { ExamplePluginOverrides } from "@btst/stack/plugins/example/client" - import { Link, useRouter, Outlet, createFileRoute } from "@tanstack/react-router" + import { Outlet, createFileRoute } from "@tanstack/react-router" // Define the shape of all plugin overrides type PluginOverrides = { @@ -735,27 +737,27 @@ In order to use BTST, your application must meet the following requirements: // Add other plugins here } + const getBaseURL = () => + typeof window !== "undefined" + ? window.location.origin + : process.env.BASE_URL || "http://localhost:3000" + export const Route = createFileRoute('/pages')({ component: Layout }) function Layout() { - const router = useRouter() const context = Route.useRouteContext() return ( basePath="/pages" + router={tanstackRouter()} + api={{ baseURL: getBaseURL(), basePath: "/api/data" }} overrides={{ example: { - navigate: (href) => router.navigate({ href }), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ) - // Add other plugin overrides here + // Only plugin-specific overrides needed here } // Add other plugins here }} @@ -771,7 +773,8 @@ In order to use BTST, your application must meet the following requirements: **Understanding Overrides:** - - **Purpose**: Injects framework-specific components via React Context. Plugin components access these overrides through `usePluginOverrides()` hook, allowing them to use your framework's `Link`, `Image`, and navigation without tight coupling and to avoid breaking the client/server boundary in frameworks like Next.js. + - **Purpose**: Injects framework-specific components via React Context. Plugin components access these overrides through `usePluginOverrides()` hook, allowing them to use your framework's `Link`, `Image`, and navigation without tight coupling and to avoid breaking the client/server boundary in frameworks like Next.js. + - **Resolution order**: per-plugin `overrides` → top-level `router`/`api` → plugin defaults. Per-plugin `Link`/`navigate`/`Image`/`apiBaseURL` values always take precedence over the router preset, so you can still override framework wiring for a single plugin as an escape hatch. - **Type Safety**: Each plugin exports its override type (e.g., `ExamplePluginOverrides`) diff --git a/packages/stack/build.config.ts b/packages/stack/build.config.ts index e26eeda8f..0a5bd1da8 100644 --- a/packages/stack/build.config.ts +++ b/packages/stack/build.config.ts @@ -49,6 +49,13 @@ export default defineBuildConfig({ "@vercel/blob/client", "@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner", + // optional peerDependencies (framework router presets) + "next", + "next/link", + "next/image", + "next/navigation", + "react-router", + "@tanstack/react-router", // test/build-time deps kept external "vitest", "@vitest/runner", @@ -66,6 +73,10 @@ export default defineBuildConfig({ "./src/client/index.ts", "./src/context/index.ts", "./src/client/components/index.tsx", + // framework router presets + "./src/next/index.tsx", + "./src/react-router/index.tsx", + "./src/tanstack/index.tsx", // plugin development entries "./src/plugins/api/index.ts", "./src/plugins/client/index.ts", diff --git a/packages/stack/knip.json b/packages/stack/knip.json index c31759d81..77a1a371a 100644 --- a/packages/stack/knip.json +++ b/packages/stack/knip.json @@ -6,6 +6,9 @@ "src/client/index.ts", "src/context/index.ts", "src/client/components/index.tsx", + "src/next/index.tsx", + "src/react-router/index.tsx", + "src/tanstack/index.tsx", "src/plugins/api/index.ts", "src/plugins/client/index.ts", "src/plugins/blog/api/index.ts", @@ -76,6 +79,9 @@ "@tailwindcss/typography", "@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner", - "@vercel/blob" + "@vercel/blob", + "next", + "react-router", + "@tanstack/react-router" ] } diff --git a/packages/stack/package.json b/packages/stack/package.json index 531f04341..38a2e5778 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -87,6 +87,36 @@ "default": "./dist/context/index.cjs" } }, + "./next": { + "import": { + "types": "./dist/next/index.d.ts", + "default": "./dist/next/index.mjs" + }, + "require": { + "types": "./dist/next/index.d.cts", + "default": "./dist/next/index.cjs" + } + }, + "./react-router": { + "import": { + "types": "./dist/react-router/index.d.ts", + "default": "./dist/react-router/index.mjs" + }, + "require": { + "types": "./dist/react-router/index.d.cts", + "default": "./dist/react-router/index.cjs" + } + }, + "./tanstack": { + "import": { + "types": "./dist/tanstack/index.d.ts", + "default": "./dist/tanstack/index.mjs" + }, + "require": { + "types": "./dist/tanstack/index.d.cts", + "default": "./dist/tanstack/index.cjs" + } + }, "./plugins/api": { "import": { "types": "./dist/plugins/api/index.d.ts", @@ -605,6 +635,15 @@ "context": [ "./dist/context/index.d.ts" ], + "next": [ + "./dist/next/index.d.ts" + ], + "react-router": [ + "./dist/react-router/index.d.ts" + ], + "tanstack": [ + "./dist/tanstack/index.d.ts" + ], "plugins/api": [ "./dist/plugins/api/index.d.ts" ], @@ -774,6 +813,7 @@ "@radix-ui/react-switch": ">=1.1.0", "@tailwindcss/typography": ">=0.5.0", "@tanstack/react-query": "^5.0.0", + "@tanstack/react-router": ">=1.0.0", "@vercel/blob": ">=0.14.0", "ai": ">=5.0.0", "better-call": ">=1.3.5", @@ -784,9 +824,11 @@ "highlight.js": ">=11.9.0", "katex": ">=0.16.0", "lucide-react": ">=0.469.0", + "next": ">=15.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "react-error-boundary": ">=4.0.0", + "react-router": ">=7.0.0", "react-hook-form": ">=7.55.0", "react-markdown": ">=9.1.0", "rehype-highlight": ">=7.0.0", @@ -808,6 +850,15 @@ }, "@aws-sdk/s3-request-presigner": { "optional": true + }, + "next": { + "optional": true + }, + "react-router": { + "optional": true + }, + "@tanstack/react-router": { + "optional": true } }, "devDependencies": { @@ -816,16 +867,20 @@ "@aws-sdk/s3-request-presigner": "^3.1011.0", "@btst/adapter-memory": "2.2.2", "@btst/yar": "1.3.0", + "@tanstack/react-router": "1.168.10", "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", "@types/slug": "^5.0.9", "@vercel/blob": "^0.27.3", "@workspace/ui": "workspace:*", "ai": "^5.0.94", "better-call": "catalog:", "knip": "^5.61.2", + "next": "16.0.10", "react": "^19.2.7", "react-dom": "^19.2.7", "react-error-boundary": "^4.1.2", + "react-router": "^7.13.1", "rollup-plugin-preserve-directives": "0.4.0", "rollup-plugin-visualizer": "^5.12.0", "tsx": "catalog:", diff --git a/packages/stack/src/__tests__/router-overrides.test.tsx b/packages/stack/src/__tests__/router-overrides.test.tsx new file mode 100644 index 000000000..95d699636 --- /dev/null +++ b/packages/stack/src/__tests__/router-overrides.test.tsx @@ -0,0 +1,193 @@ +import { describe, expect, it } from "vitest"; +import { renderToString } from "react-dom/server"; +import type { ComponentType } from "react"; +import { StackProvider, usePluginOverrides } from "../context"; +import type { StackApiConfig, StackRouterConfig } from "../context"; + +/** + * Renders StackProvider with a probe child that captures the resolved + * overrides for the given plugin, without needing a DOM. + */ +function resolveOverrides({ + pluginName, + overrides, + router, + api, + defaultValues, +}: { + pluginName: string; + overrides?: Record; + router?: StackRouterConfig; + api?: StackApiConfig; + defaultValues?: Record; +}): any { + let captured: any; + + function Probe() { + captured = usePluginOverrides(pluginName, defaultValues); + return null; + } + + renderToString( + + + , + ); + + return captured; +} + +const RouterLink: ComponentType = () => null; +const PluginLink: ComponentType = () => null; + +describe("top-level router/api override resolution", () => { + it("behaves exactly as before when no router/api is provided", () => { + const blogOverrides = { navigate: () => {}, apiBaseURL: "x" }; + const resolved = resolveOverrides({ + pluginName: "blog", + overrides: { blog: blogOverrides }, + }); + + // Without router/api or defaults, the plugin overrides object is + // returned as-is (same reference), matching the previous behavior. + expect(resolved).toBe(blogOverrides); + }); + + it("returns undefined for unconfigured plugins when no router/api is provided", () => { + const resolved = resolveOverrides({ + pluginName: "blog", + overrides: {}, + }); + expect(resolved).toBeUndefined(); + }); + + it("applies top-level router fields to every plugin", () => { + const navigate = () => {}; + const refresh = () => {}; + const resolved = resolveOverrides({ + pluginName: "blog", + overrides: { blog: {} }, + router: { Link: RouterLink, navigate, refresh }, + }); + + expect(resolved.Link).toBe(RouterLink); + expect(resolved.navigate).toBe(navigate); + expect(resolved.refresh).toBe(refresh); + }); + + it("applies router fields even to plugins with no overrides block", () => { + const resolved = resolveOverrides({ + pluginName: "kanban", + overrides: {}, + router: { Link: RouterLink }, + }); + + expect(resolved.Link).toBe(RouterLink); + }); + + it("maps api config to apiBaseURL/apiBasePath", () => { + const resolved = resolveOverrides({ + pluginName: "blog", + overrides: { blog: {} }, + api: { baseURL: "https://example.com", basePath: "/api/data" }, + }); + + expect(resolved.apiBaseURL).toBe("https://example.com"); + expect(resolved.apiBasePath).toBe("/api/data"); + }); + + it("per-plugin overrides beat the top-level router and api", () => { + const routerNavigate = () => {}; + const pluginNavigate = () => {}; + const resolved = resolveOverrides({ + pluginName: "blog", + overrides: { + blog: { + Link: PluginLink, + navigate: pluginNavigate, + apiBaseURL: "https://plugin.example.com", + }, + }, + router: { Link: RouterLink, navigate: routerNavigate }, + api: { baseURL: "https://example.com", basePath: "/api/data" }, + }); + + expect(resolved.Link).toBe(PluginLink); + expect(resolved.navigate).toBe(pluginNavigate); + expect(resolved.apiBaseURL).toBe("https://plugin.example.com"); + // Fields not overridden per plugin still come from the top level + expect(resolved.apiBasePath).toBe("/api/data"); + }); + + it("top-level router/api beat hook defaultValues", () => { + const routerNavigate = () => {}; + const defaultNavigate = () => {}; + const resolved = resolveOverrides({ + pluginName: "blog", + overrides: { blog: {} }, + router: { navigate: routerNavigate }, + api: { baseURL: "https://example.com", basePath: "/api/data" }, + defaultValues: { + navigate: defaultNavigate, + apiBaseURL: "https://default.example.com", + localization: { TITLE: "Default" }, + }, + }); + + expect(resolved.navigate).toBe(routerNavigate); + expect(resolved.apiBaseURL).toBe("https://example.com"); + // Defaults not managed by router/api survive + expect(resolved.localization).toEqual({ TITLE: "Default" }); + }); + + it("undefined router fields do not clobber hook defaults", () => { + const defaultNavigate = () => {}; + const resolved = resolveOverrides({ + pluginName: "blog", + overrides: { blog: {} }, + router: { Link: RouterLink, navigate: undefined }, + defaultValues: { navigate: defaultNavigate }, + }); + + expect(resolved.Link).toBe(RouterLink); + expect(resolved.navigate).toBe(defaultNavigate); + }); + + it("evaluates the preset useRouter hook and merges its result over static fields", () => { + const staticNavigate = () => {}; + const hookNavigate = () => {}; + const hookRefresh = () => {}; + const resolved = resolveOverrides({ + pluginName: "blog", + overrides: { blog: {} }, + router: { + Link: RouterLink, + navigate: staticNavigate, + useRouter: () => ({ navigate: hookNavigate, refresh: hookRefresh }), + }, + }); + + // Hook result wins over the static preset field + expect(resolved.navigate).toBe(hookNavigate); + expect(resolved.refresh).toBe(hookRefresh); + // Static fields not returned by the hook survive + expect(resolved.Link).toBe(RouterLink); + }); + + it("works without an overrides prop at all", () => { + const navigate = () => {}; + const resolved = resolveOverrides({ + pluginName: "blog", + router: { navigate }, + api: { baseURL: "https://example.com", basePath: "/api/data" }, + }); + + expect(resolved.navigate).toBe(navigate); + expect(resolved.apiBaseURL).toBe("https://example.com"); + }); +}); diff --git a/packages/stack/src/context/index.ts b/packages/stack/src/context/index.ts index 1a8f850f5..1e4fdb3d8 100644 --- a/packages/stack/src/context/index.ts +++ b/packages/stack/src/context/index.ts @@ -1 +1,2 @@ export * from "./provider"; +export * from "./router"; diff --git a/packages/stack/src/context/provider.tsx b/packages/stack/src/context/provider.tsx index 4dcdb07e2..1adfc4217 100644 --- a/packages/stack/src/context/provider.tsx +++ b/packages/stack/src/context/provider.tsx @@ -1,5 +1,11 @@ "use client"; import { createContext, useContext, type ReactNode } from "react"; +import type { + StackApiConfig, + StackRouter, + StackRouterConfig, + WithOptionalRouterOverrides, +} from "./router"; /** * Context value that provides plugin-specific overrides @@ -14,10 +20,81 @@ interface StackContextValue> { * The base path where the client router is mounted. */ basePath: string; + /** + * Resolved top-level router (static preset fields merged with the + * preset's `useRouter` hook result). + */ + router?: StackRouter; + /** + * Top-level API config applied to all plugins. + */ + api?: StackApiConfig; } const StackContext = createContext | null>(null); +/** + * The `overrides` prop shape for `StackProvider`: per plugin, the fields + * managed by the top-level `router` / `api` props become optional, and + * plugin blocks whose remaining fields are all optional can be omitted + * entirely. + */ +export type StackProviderOverrides< + TPluginOverrides extends Record, +> = { + [K in keyof TPluginOverrides]?: WithOptionalRouterOverrides< + TPluginOverrides[K] + >; +}; + +/** Removes keys whose value is `undefined` so they don't clobber lower layers in spreads. */ +function stripUndefined>(obj: T): Partial { + const result: Record = {}; + for (const key of Object.keys(obj)) { + if (obj[key] !== undefined) { + result[key] = obj[key]; + } + } + return result as Partial; +} + +function resolveStaticRouter( + router: StackRouterConfig | undefined, +): StackRouter | undefined { + if (!router) return undefined; + const { useRouter: _useRouter, ...staticFields } = router; + return stripUndefined(staticFields); +} + +/** + * Internal component that evaluates the router preset's `useRouter` hook. + * Rendered only when the hook exists, so the hook itself is always called + * unconditionally within this component. + */ +function RouterBridge({ + useRouter, + staticRouter, + value, + children, +}: { + useRouter: () => StackRouter; + staticRouter: StackRouter | undefined; + value: Omit, "router">; + children?: ReactNode; +}) { + const hookRouter = useRouter(); + const router: StackRouter = { + ...staticRouter, + ...stripUndefined(hookRouter), + }; + + return ( + + {children} + + ); +} + /** * Provider component for BTST context * Provides type-safe access to plugin-specific overrides @@ -46,6 +123,26 @@ const StackContext = createContext | null>(null); * {children} * * ``` + * + * With a framework router preset, the shared `Link`/`navigate`/`refresh`/ + * `Image` wiring and API config move to the top level and per-plugin + * overrides only carry genuinely plugin-specific values: + * + * @example + * ```tsx + * import { nextRouter } from "@btst/stack/next"; + * + * + * basePath="/pages" + * router={nextRouter()} + * api={{ baseURL, basePath: "/api/data" }} + * overrides={{ + * blog: { uploadImage }, + * }} + * > + * {children} + * + * ``` */ export function StackProvider< TPluginOverrides extends Record = Record, @@ -53,18 +150,38 @@ export function StackProvider< children, overrides, basePath, + router, + api, }: { children?: ReactNode; - overrides: TPluginOverrides; + overrides?: StackProviderOverrides; basePath: string; + router?: StackRouterConfig; + api?: StackApiConfig; }) { - const value: StackContextValue = { - overrides, + const staticRouter = resolveStaticRouter(router); + const value: Omit, "router"> = { + overrides: overrides ?? {}, basePath, + api, }; + if (router?.useRouter) { + return ( + + {children} + + ); + } + return ( - {children} + + {children} + ); } @@ -138,11 +255,33 @@ export function usePluginOverrides< const pluginOverrides = context.overrides[pluginName]; - // If defaults are provided, merge them with plugin overrides - // This ensures default properties exist even if plugin is partially configured - const overrides = defaultValues - ? { ...defaultValues, ...pluginOverrides } - : pluginOverrides; + // Resolution order (lowest to highest precedence): + // hook defaults -> top-level router/api -> per-plugin overrides + const { router, api } = context; + if (!router && !api) { + // No top-level router/api configured — behave exactly as before + const overrides = defaultValues + ? { ...defaultValues, ...pluginOverrides } + : pluginOverrides; + return overrides as OverridesResult; + } + + const routerApiLayer = stripUndefined({ + Link: router?.Link, + Image: router?.Image, + navigate: router?.navigate, + refresh: router?.refresh, + getSearchParams: router?.getSearchParams, + setSearchParams: router?.setSearchParams, + apiBaseURL: api?.baseURL, + apiBasePath: api?.basePath, + }); + + const overrides = { + ...defaultValues, + ...routerApiLayer, + ...pluginOverrides, + }; return overrides as OverridesResult; } diff --git a/packages/stack/src/context/router.tsx b/packages/stack/src/context/router.tsx new file mode 100644 index 000000000..a18e04022 --- /dev/null +++ b/packages/stack/src/context/router.tsx @@ -0,0 +1,97 @@ +import type { ComponentType } from "react"; + +/** + * Framework routing primitives shared by all plugins. + * + * These are the fields that every plugin override block used to re-wire + * individually (`Link`, `navigate`, `refresh`, `Image`). Providing them once + * via the top-level `router` prop on `StackProvider` applies them to every + * plugin; per-plugin overrides still take precedence. + */ +export interface StackRouter { + /** + * Link component for navigation + */ + Link?: ComponentType & Record>; + /** + * Image component for displaying images + */ + Image?: ComponentType< + React.ImgHTMLAttributes & Record + >; + /** + * Navigation function for programmatic navigation + */ + navigate?: (path: string) => void | Promise; + /** + * Refresh function to invalidate server-side cache (e.g., Next.js router.refresh()) + */ + refresh?: () => void | Promise; + /** + * Read the current URL search params + */ + getSearchParams?: () => URLSearchParams; + /** + * Replace the current URL search params + */ + setSearchParams?: ( + next: URLSearchParams, + opts?: { replace?: boolean }, + ) => void; +} + +/** + * Config accepted by the `router` prop on `StackProvider`. + * + * Framework presets (`nextRouter()`, `reactRouter()`, `tanstackRouter()`) are + * plain objects created anywhere — including module scope — so fields that + * need framework hooks are produced by the optional `useRouter` hook, which + * `StackProvider` evaluates internally. Hook results are merged over the + * static fields. + */ +export interface StackRouterConfig extends StackRouter { + /** + * Optional hook evaluated inside `StackProvider`. Use this for router + * fields that must be derived from framework hooks (e.g. `useNavigate`). + */ + useRouter?: () => StackRouter; +} + +/** + * Top-level API config applied to all plugins. + * Maps to the `apiBaseURL` / `apiBasePath` fields of each plugin's overrides. + */ +export interface StackApiConfig { + /** + * API base URL (e.g. `https://example.com`) + */ + baseURL: string; + /** + * API base path (e.g. `/api/data`) + */ + basePath: string; +} + +/** + * Override keys that are managed by the top-level `router` / `api` props. + */ +export type RouterManagedOverrideKeys = + | "Link" + | "Image" + | "navigate" + | "refresh" + | "getSearchParams" + | "setSearchParams" + | "apiBaseURL" + | "apiBasePath"; + +/** + * Makes the router/api-managed fields of a plugin overrides interface + * optional, so consumers wiring `router` / `api` at the top level can omit + * them per plugin without type errors. + */ +export type WithOptionalRouterOverrides = Omit< + T, + Extract +> & + Partial>>; diff --git a/packages/stack/src/next/index.tsx b/packages/stack/src/next/index.tsx new file mode 100644 index 000000000..2d4af4c3d --- /dev/null +++ b/packages/stack/src/next/index.tsx @@ -0,0 +1,102 @@ +"use client"; +import NextImage from "next/image"; +import NextLink from "next/link"; +import { useRouter } from "next/navigation"; +import { useMemo } from "react"; +import type { StackRouter, StackRouterConfig } from "../context/router"; + +function NextLinkWrapper({ + href, + ...props +}: React.ComponentProps<"a"> & Record) { + return ; +} + +/** + * Next.js Image wrapper for plugins. + * Handles both cases: with explicit dimensions or using fill mode. + */ +function NextImageWrapper(props: React.ImgHTMLAttributes) { + const { alt = "", src = "", width, height, ...rest } = props; + + // Use fill mode if width or height are not provided + if (!width || !height) { + return ( + + + + ); + } + + return ( + + ); +} + +// Reads window.location.search instead of Next's useSearchParams() hook to +// avoid forcing a Suspense/CSR bailout during static generation. Returns +// empty params on the server. +function getSearchParams(): URLSearchParams { + return new URLSearchParams( + typeof window !== "undefined" ? window.location.search : "", + ); +} + +function useNextStackRouter(): StackRouter { + const router = useRouter(); + + return useMemo( + () => ({ + navigate: (path: string) => { + router.push(path); + }, + refresh: () => { + router.refresh(); + }, + setSearchParams: ( + next: URLSearchParams, + opts?: { replace?: boolean }, + ) => { + const query = next.toString(); + const path = `${window.location.pathname}${query ? `?${query}` : ""}`; + if (opts?.replace) { + router.replace(path); + } else { + router.push(path); + } + }, + }), + [router], + ); +} + +/** + * Router preset for Next.js (App Router). + * + * @example + * ```tsx + * import { nextRouter } from "@btst/stack/next"; + * + * + * ``` + */ +export function nextRouter(): StackRouterConfig { + return { + Link: NextLinkWrapper, + Image: NextImageWrapper, + getSearchParams, + useRouter: useNextStackRouter, + }; +} diff --git a/packages/stack/src/react-router/index.tsx b/packages/stack/src/react-router/index.tsx new file mode 100644 index 000000000..27b0b40f1 --- /dev/null +++ b/packages/stack/src/react-router/index.tsx @@ -0,0 +1,63 @@ +"use client"; +import { useMemo } from "react"; +import { + Link as ReactRouterLink, + useNavigate, + useRevalidator, + useSearchParams, +} from "react-router"; +import type { StackRouter, StackRouterConfig } from "../context/router"; + +function ReactRouterLinkWrapper({ + href, + children, + ...props +}: React.ComponentProps<"a"> & Record) { + return ( + + {children} + + ); +} + +function useReactRouterStackRouter(): StackRouter { + const navigate = useNavigate(); + const { revalidate } = useRevalidator(); + const [searchParams, setSearchParams] = useSearchParams(); + + return useMemo( + () => ({ + navigate: (path: string) => { + void navigate(path); + }, + refresh: () => { + void revalidate(); + }, + getSearchParams: () => new URLSearchParams(searchParams), + setSearchParams: ( + next: URLSearchParams, + opts?: { replace?: boolean }, + ) => { + setSearchParams(next, { replace: opts?.replace }); + }, + }), + [navigate, revalidate, searchParams, setSearchParams], + ); +} + +/** + * Router preset for React Router (v7). + * + * @example + * ```tsx + * import { reactRouter } from "@btst/stack/react-router"; + * + * + * ``` + */ +export function reactRouter(): StackRouterConfig { + return { + Link: ReactRouterLinkWrapper, + useRouter: useReactRouterStackRouter, + }; +} diff --git a/packages/stack/src/tanstack/index.tsx b/packages/stack/src/tanstack/index.tsx new file mode 100644 index 000000000..9451bf423 --- /dev/null +++ b/packages/stack/src/tanstack/index.tsx @@ -0,0 +1,61 @@ +"use client"; +import { Link as TanStackLink, useRouter } from "@tanstack/react-router"; +import { useMemo } from "react"; +import type { StackRouter, StackRouterConfig } from "../context/router"; + +function TanStackLinkWrapper({ + href, + children, + ...props +}: React.ComponentProps<"a"> & Record) { + return ( + + {children} + + ); +} + +function useTanStackStackRouter(): StackRouter { + const router = useRouter(); + + return useMemo( + () => ({ + navigate: (path: string) => { + router.navigate({ href: path }); + }, + refresh: () => { + router.invalidate(); + }, + getSearchParams: () => + new URLSearchParams(router.state.location.searchStr ?? ""), + setSearchParams: ( + next: URLSearchParams, + opts?: { replace?: boolean }, + ) => { + const query = next.toString(); + router.navigate({ + href: `${router.state.location.pathname}${query ? `?${query}` : ""}`, + replace: opts?.replace, + }); + }, + }), + [router], + ); +} + +/** + * Router preset for TanStack Router / TanStack Start. + * + * @example + * ```tsx + * import { tanstackRouter } from "@btst/stack/tanstack"; + * + * + * ``` + */ +export function tanstackRouter(): StackRouterConfig { + return { + Link: TanStackLinkWrapper, + useRouter: useTanStackStackRouter, + }; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cebefa757..545c39747 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,7 +67,143 @@ importers: version: 3.6.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3)) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.22.4)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.22.4)(yaml@2.8.2) + + codegen-projects/tanstack: + dependencies: + '@ai-sdk/openai': + specifier: ^2.0.68 + version: 2.0.110(zod@4.4.3) + '@btst/adapter-memory': + specifier: ^2.2.2 + version: 2.2.2(e0348a04c834c515f6e6974fb4bbed6d) + '@btst/stack': + specifier: workspace:* + version: link:../../packages/stack + '@fontsource-variable/geist': + specifier: ^5.2.9 + version: 5.2.9 + '@tailwindcss/vite': + specifier: ^4.2.1 + version: 4.3.2(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + '@tanstack/react-devtools': + specifier: latest + version: 0.10.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12) + '@tanstack/react-query': + specifier: ^5.90.2 + version: 5.90.10(react@19.2.7) + '@tanstack/react-query-devtools': + specifier: ^5.90.2 + version: 5.101.2(@tanstack/react-query@5.90.10(react@19.2.7))(react@19.2.7) + '@tanstack/react-router': + specifier: 1.168.10 + version: 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-router-devtools': + specifier: latest + version: 1.167.0(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.168.9)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-router-ssr-query': + specifier: 1.167.1 + version: 1.167.1(@tanstack/query-core@5.90.10)(@tanstack/react-query@5.90.10(react@19.2.7))(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.168.9)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-start': + specifier: 1.167.16 + version: 1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + '@tanstack/router-plugin': + specifier: 1.167.12 + version: 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + '@vitejs/plugin-react': + specifier: ^5.2.0 + version: 5.2.0(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + ai: + specifier: ^5.0.94 + version: 5.0.94(zod@4.4.3) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^0.545.0 + version: 0.545.0(react@19.2.7) + next-themes: + specifier: ^0.4.6 + version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + nitro: + specifier: 3.0.260603-beta + version: 3.0.260603-beta(@electric-sql/pglite@0.3.15)(@vercel/blob@0.27.3)(chokidar@4.0.3)(dotenv@17.2.3)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(rollup@4.53.2)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + radix-ui: + specifier: ^1.6.1 + version: 1.6.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: + specifier: 19.2.7 + version: 19.2.7 + react-dom: + specifier: 19.2.7 + version: 19.2.7(react@19.2.7) + shadcn: + specifier: ^4.13.0 + version: 4.13.0(typescript@6.0.3) + sonner: + specifier: ^2.0.7 + version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + tailwind-merge: + specifier: ^3.6.0 + version: 3.6.0 + tailwindcss: + specifier: ^4 + version: 4.2.2 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 + vite: + specifier: 7.3.1 + version: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@6.0.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + zod: + specifier: 4.4.3 + version: 4.4.3 + devDependencies: + '@tanstack/devtools-vite': + specifier: latest + version: 0.8.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + '@tanstack/eslint-config': + specifier: latest + version: 0.4.0(@typescript-eslint/utils@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@testing-library/dom': + specifier: ^10.4.1 + version: 10.4.1 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@types/node': + specifier: ^22 + version: 22.20.0 + '@types/react': + specifier: ^19 + version: 19.2.14 + '@types/react-dom': + specifier: ^19 + version: 19.2.3(@types/react@19.2.14) + eslint: + specifier: ^9 + version: 9.39.4(jiti@2.7.0) + jsdom: + specifier: ^28 + version: 28.1.0(@noble/hashes@2.0.1) + prettier: + specifier: ^3.8.3 + version: 3.8.4 + prettier-plugin-tailwindcss: + specifier: ^0.8.0 + version: 0.8.0(prettier@3.8.4) + typescript: + specifier: ^6 + version: 6.0.3 + vitest: + specifier: ^4 + version: 4.1.9(@opentelemetry/api@1.9.0)(@types/node@22.20.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@22.20.0)(typescript@6.0.3))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) docs: dependencies: @@ -88,7 +224,7 @@ importers: version: 3.0.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(fumadocs-core@16.0.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@types/react@19.2.14)(lucide-react@0.522.0(react@19.2.7))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)) fumadocs-mdx: specifier: 13.0.6 - version: 13.0.6(fumadocs-core@16.0.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@types/react@19.2.14)(lucide-react@0.522.0(react@19.2.7))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + version: 13.0.6(fumadocs-core@16.0.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@types/react@19.2.14)(lucide-react@0.522.0(react@19.2.7))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.0.3)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) fumadocs-typescript: specifier: 4.0.13 version: 4.0.13(@types/react@19.2.14)(fumadocs-core@16.0.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@types/react@19.2.14)(lucide-react@0.522.0(react@19.2.7))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7))(fumadocs-ui@16.0.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(lucide-react@0.522.0(react@19.2.7))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(tailwindcss@4.2.2))(typescript@5.9.3) @@ -100,7 +236,7 @@ importers: version: 0.522.0(react@19.2.7) next: specifier: 16.0.10 - version: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: specifier: 19.2.7 version: 19.2.7 @@ -137,7 +273,7 @@ importers: version: 8.57.1 eslint-config-next: specifier: 15.3.4 - version: 15.3.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + version: 15.3.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) postcss: specifier: ^8.5.6 version: 8.5.6 @@ -198,13 +334,13 @@ importers: version: 3.6.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3)) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) packages/stack: dependencies: '@btst/db': specifier: 2.2.2 - version: 2.2.2(7010515aebf7f15e3f6fd94d16578921) + version: 2.2.2(84c0473dacd82373aa0e1c019674ae07) '@hookform/resolvers': specifier: '>=5.0.0' version: 5.2.2(react-hook-form@7.66.1(react@19.2.7)) @@ -304,13 +440,19 @@ importers: version: 3.1011.0 '@btst/adapter-memory': specifier: 2.2.2 - version: 2.2.2(3f1048c33dcc3a34f5463c3b01c66883) + version: 2.2.2(1cc8778580309809cf6cdfc6c185225b) '@btst/yar': specifier: 1.3.0 version: 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react@19.2.7) + '@tanstack/react-router': + specifier: 1.168.10 + version: 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@types/react': specifier: ^19.0.0 version: 19.2.14 + '@types/react-dom': + specifier: ^19.0.0 + version: 19.2.3(@types/react@19.2.14) '@types/slug': specifier: ^5.0.9 version: 5.0.9 @@ -329,6 +471,9 @@ importers: knip: specifier: ^5.61.2 version: 5.86.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.0)(typescript@5.9.3) + next: + specifier: 16.0.10 + version: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: specifier: 19.2.7 version: 19.2.7 @@ -338,6 +483,9 @@ importers: react-error-boundary: specifier: ^4.1.2 version: 4.1.2(react@19.2.7) + react-router: + specifier: ^7.13.1 + version: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) rollup-plugin-preserve-directives: specifier: 0.4.0 version: 0.4.0(rollup@4.53.2) @@ -355,7 +503,7 @@ importers: version: 3.6.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3)) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) zod: specifier: 4.4.3 version: 4.4.3 @@ -614,7 +762,7 @@ importers: version: 8.5.6 postcss-cli: specifier: ^11.0.1 - version: 11.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.22.4) + version: 11.0.1(jiti@2.7.0)(postcss@8.5.6)(tsx@4.22.4) tailwindcss: specifier: ^4.1.11 version: 4.2.2 @@ -650,13 +798,13 @@ importers: version: 1.7.0(react@19.2.7) next: specifier: 16.0.10 - version: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) nuqs: specifier: ^2.8.9 - version: 2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) + version: 2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) react: specifier: 19.2.7 version: 19.2.7 @@ -712,16 +860,32 @@ packages: peerDependencies: zod: 4.4.3 + '@ai-sdk/openai@2.0.110': + resolution: {integrity: sha512-PVbq0vXo6LVBCKeIOiDsOfrUIL2IOkMUipWP53SJZppDo594kbu5BFs/NdwnJMAX4q6EM6sGPVv751RN9PrZ9A==} + engines: {node: '>=18'} + peerDependencies: + zod: 4.4.3 + '@ai-sdk/provider-utils@3.0.17': resolution: {integrity: sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw==} engines: {node: '>=18'} peerDependencies: zod: 4.4.3 + '@ai-sdk/provider-utils@3.0.28': + resolution: {integrity: sha512-bXlX1WX7E50a2N+AJW+1a/x63m52aPhm+6xYe5THxWrx9vW9NR7E2Ay+1G1ndlCdMdYKo2Fnsd7kBhuyQPaphw==} + engines: {node: '>=18'} + peerDependencies: + zod: 4.4.3 + '@ai-sdk/provider@2.0.0': resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} engines: {node: '>=18'} + '@ai-sdk/provider@2.0.3': + resolution: {integrity: sha512-h88OPkavHTiN9tMn2l5awAznGB0lXzjcLhgR1/rvjB2zlLprsNxbM2tt6OJsHUxduLC3klq0/eqaSf6fX5XVww==} + engines: {node: '>=18'} + '@ai-sdk/react@2.0.94': resolution: {integrity: sha512-eVhV6O4uUn/aIckiRomSukovzMAqARsiLn3X0s92p/EpO+LGDixUcWsdw58hym6VQtM3r5f/qIYzhSFv6gnirQ==} engines: {node: '>=18'} @@ -1061,12 +1225,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.27.1': - resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.29.7': resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} engines: {node: '>=6.9.0'} @@ -1079,6 +1237,18 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-typescript@7.28.5': resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} engines: {node: '>=6.9.0'} @@ -1099,10 +1269,6 @@ packages: resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} - engines: {node: '>=6.9.0'} - '@babel/template@7.29.7': resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} @@ -1507,15 +1673,9 @@ packages: '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@emnapi/core@1.9.1': - resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} - '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@emnapi/wasi-threads@1.2.0': - resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} - '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} @@ -1997,14 +2157,51 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@2.1.4': resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@10.0.1': + resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + '@eslint/js@8.57.1': resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@exodus/bytes@1.15.1': resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -2039,6 +2236,9 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + '@fontsource-variable/geist@5.2.9': + resolution: {integrity: sha512-TP+QSBG3wxKGPE33CbMy/L0Nu3qvJ6Fy81Yc4LnQ95xH+i+cfEp8fyU8/kfV14YwszxIFPhnoMTbjL71waVpyQ==} + '@formatjs/intl-localematcher@0.6.2': resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} @@ -2053,6 +2253,18 @@ packages: peerDependencies: react-hook-form: ^7.55.0 + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + '@humanwhocodes/config-array@0.13.0': resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} engines: {node: '>=10.10.0'} @@ -2066,6 +2278,10 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + '@img/colour@1.0.0': resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} engines: {node: '>=18'} @@ -2638,6 +2854,136 @@ packages: resolution: {integrity: sha512-scSmQBD8eANlMUOglxHrN1JdSW8tDghsPuS83otqealBiIeMukCQMOf/wc0JJjDXomqwNdEQFLXLGHrU6PGxuA==} engines: {node: '>= 20.0.0'} + '@oxc-parser/binding-android-arm-eabi@0.120.0': + resolution: {integrity: sha512-WU3qtINx802wOl8RxAF1v0VvmC2O4D9M8Sv486nLeQ7iPHVmncYZrtBhB4SYyX+XZxj2PNnCcN+PW21jHgiOxg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.120.0': + resolution: {integrity: sha512-SEf80EHdhlbjZEgzeWm0ZA/br4GKMenDW3QB/gtyeTV1gStvvZeFi40ioHDZvds2m4Z9J1bUAUL8yn1/+A6iGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.120.0': + resolution: {integrity: sha512-xVrrbCai8R8CUIBu3CjryutQnEYhZqs1maIqDvtUCFZb8vY33H7uh9mHpL3a0JBIKoBUKjPH8+rzyAeXnS2d6A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.120.0': + resolution: {integrity: sha512-xyHBbnJ6mydnQUH7MAcafOkkrNzQC6T+LXgDH/3InEq2BWl/g424IMRiJVSpVqGjB+p2bd0h0WRR8iIwzjU7rw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.120.0': + resolution: {integrity: sha512-UMnVRllquXUYTeNfFKmxTTEdZ/ix1nLl0ducDzMSREoWYGVIHnOOxoKMWlCOvRr9Wk/HZqo2rh1jeumbPGPV9A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': + resolution: {integrity: sha512-tkvn2CQ7QdcsMnpfiX3fd3wA3EFsWKYlcQzq9cFw/xc89Al7W6Y4O0FgLVkVQpo0Tnq/qtE1XfkJOnRRA9S/NA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': + resolution: {integrity: sha512-WN5y135Ic42gQDk9grbwY9++fDhqf8knN6fnP+0WALlAUh4odY/BDK1nfTJRSfpJD9P3r1BwU0m3pW2DU89whQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.120.0': + resolution: {integrity: sha512-1GgQBCcXvFMw99EPdMy+4NZ3aYyXsxjf9kbUUg8HuAy3ZBXzOry5KfFEzT9nqmgZI1cuetvApkiJBZLAPo8uaw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-arm64-musl@0.120.0': + resolution: {integrity: sha512-gmMQ70gsPdDBgpcErvJEoWNBr7bJooSLlvOBVBSGfOzlP5NvJ3bFvnUeZZ9d+dPrqSngtonf7nyzWUTUj/U+lw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': + resolution: {integrity: sha512-T/kZuU0ajop0xhzVMwH5r3srC9Nqup5HaIo+3uFjIN5uPxa0LvSxC1ZqP4aQGJVW5G0z8/nCkjIfSMS91P/wzw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': + resolution: {integrity: sha512-vn21KXLAXzaI3N5CZWlBr1iWeXLl9QFIMor7S1hUjUGTeUuWCoE6JZB040/ZNDwf+JXPX8Ao9KbmJq9FMC2iGw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-musl@0.120.0': + resolution: {integrity: sha512-SUbUxlar007LTGmSLGIC5x/WJvwhdX+PwNzFJ9f/nOzZOrCFbOT4ikt7pJIRg1tXVsEfzk5mWpGO1NFiSs4PIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-s390x-gnu@0.120.0': + resolution: {integrity: sha512-hYiPJTxyfJY2+lMBFk3p2bo0R9GN+TtpPFlRqVchL1qvLG+pznstramHNvJlw9AjaoRUHwp9IKR7UZQnRPGjgQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-gnu@0.120.0': + resolution: {integrity: sha512-q+5jSVZkprJCIy3dzJpApat0InJaoxQLsJuD6DkX8hrUS61z2lHQ1Fe9L2+TYbKHXCLWbL0zXe7ovkIdopBGMQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-musl@0.120.0': + resolution: {integrity: sha512-D9QDDZNnH24e7X4ftSa6ar/2hCavETfW3uk0zgcMIrZNy459O5deTbWrjGzZiVrSWigGtlQwzs2McBP0QsfV1w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-openharmony-arm64@0.120.0': + resolution: {integrity: sha512-TBU8ZwOUWAOUWVfmI16CYWbvh4uQb9zHnGBHsw5Cp2JUVG044OIY1CSHODLifqzQIMTXvDvLzcL89GGdUIqNrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.120.0': + resolution: {integrity: sha512-WG/FOZgDJCpJnuF3ToG/K28rcOmSY7FmFmfBKYb2fmLyhDzPpUldFGV7/Fz4ru0Iz/v4KPmf8xVgO8N3lO4KHA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.120.0': + resolution: {integrity: sha512-1T0HKGcsz/BKo77t7+89L8Qvu4f9DoleKWHp3C5sJEcbCjDOLx3m9m722bWZTY+hANlUEs+yjlK+lBFsA+vrVQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.120.0': + resolution: {integrity: sha512-L7vfLzbOXsjBXV0rv/6Y3Jd9BRjPeCivINZAqrSyAOZN3moCopDN+Psq9ZrGNZtJzP8946MtlRFZ0Als0wBCOw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.120.0': + resolution: {integrity: sha512-ys+upfqNtSu58huAhJMBKl3XCkGzyVFBlMlGPzHeFKgpFF/OdgNs1MMf8oaJIbgMH8ZxgGF7qfue39eJohmKIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.120.0': + resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} + '@oxc-project/types@0.134.0': resolution: {integrity: sha512-T0xuRRKrQFmocH8y+jGfpmSkGcheaJExY9lEihmR1Gm2aH+75B8CzgU2rABRQSzzDxLjZ15Sc0bRVLj5lVeNXQ==} @@ -2905,11 +3251,17 @@ packages: '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} + '@radix-ui/number@1.1.2': + resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==} + '@radix-ui/primitive@1.1.3': resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} - '@radix-ui/react-accessible-icon@1.1.7': - resolution: {integrity: sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A==} + '@radix-ui/primitive@1.1.4': + resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} + + '@radix-ui/react-accessible-icon@1.1.11': + resolution: {integrity: sha512-HQDOFTKwSnmUij6l54wYJJtxTAnxI71+YJLOrjm2ladFB8HAV5Jt7hwaZPhWTGBkYoW4+ZAOfNZrLDh/qvxSYA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2921,8 +3273,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-accordion@1.2.12': - resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} + '@radix-ui/react-accessible-icon@1.1.7': + resolution: {integrity: sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2934,8 +3286,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-alert-dialog@1.1.15': - resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==} + '@radix-ui/react-accordion@1.2.12': + resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2947,8 +3299,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-arrow@1.1.7': - resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + '@radix-ui/react-accordion@1.2.15': + resolution: {integrity: sha512-24Zz/0SYx8F2bSVThBnQrdJs2VbKelyuJordcFRRdA0fRAhrq/wSegGCqaQz34VQoiWqSMGYCYXEhynLSlyQlg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2960,8 +3312,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-aspect-ratio@1.1.7': - resolution: {integrity: sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==} + '@radix-ui/react-alert-dialog@1.1.15': + resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2973,8 +3325,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-avatar@1.1.10': - resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} + '@radix-ui/react-alert-dialog@1.1.18': + resolution: {integrity: sha512-6c2cXpNlAgHDhKguK24XcWHHayMpK+lk7/WwBXBco+ZJ4Dv7xP++GBM280KgTD/HCRu3jSdfe8WQiZssonYaIA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2986,8 +3338,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-avatar@1.1.11': - resolution: {integrity: sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==} + '@radix-ui/react-arrow@1.1.11': + resolution: {integrity: sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2999,8 +3351,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-checkbox@1.3.3': - resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3012,8 +3364,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collapsible@1.1.12': - resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} + '@radix-ui/react-aspect-ratio@1.1.11': + resolution: {integrity: sha512-IUAhIVpBUvP5NNICjlaB1OFmtRLGqQqTF3ZOSGPoq3XeLXRFtHiWTRxSVEULgOd9GQR2c7tsYqDnhUennapZnw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3025,8 +3377,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.7': - resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + '@radix-ui/react-aspect-ratio@1.1.7': + resolution: {integrity: sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3038,17 +3390,21 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + '@radix-ui/react-avatar@1.1.10': + resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: 19.2.7 + react-dom: 19.2.7 peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-context-menu@2.2.16': - resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==} + '@radix-ui/react-avatar@1.1.11': + resolution: {integrity: sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3060,8 +3416,143 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-context@1.1.2': - resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + '@radix-ui/react-avatar@1.2.1': + resolution: {integrity: sha512-+8PWoLLZv3AVb5m0pvoiOca/bQGzc9vPVb+982HB2x3Un0DpYEPM3zLMl4oqRpBsocJuNqLkiv/HXTnTrlwr4g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-checkbox@1.3.3': + resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-checkbox@1.3.6': + resolution: {integrity: sha512-eUEUoGMDpfkgHWSE97ZZaUJtzR1M7EKnNIpD1Q16+8JR9NWghcaqMulx9PuCQ720w0UclfYn6FEbCdd5Hx087g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collapsible@1.1.12': + resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collapsible@1.1.15': + resolution: {integrity: sha512-8A1zibu5skAQ+UVbaeNH5hVMibiFCRJzgMuM14LTWGttnTZKQL9jwYnhAbHRuxrtCqPXa4JvvnVUq1pTNgyZYw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.11': + resolution: {integrity: sha512-djW9+zeg137KQdlPtmE8xnaD+K2rcXXMWFrSg0hsmYZ6HRbdTA7tDHFgpaW9+huWVEu0RCabL+985T4TA0BE7g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context-menu@2.2.16': + resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-context-menu@2.3.2': + resolution: {integrity: sha512-qzsA/ZPhF6yMxBOTIk1nlCkoy2mswSbwYL+ErBa2iP0s4WWrlxmczArYqMcpVfEjmM7KJj/ADPXky0yZfbSxtQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: '@types/react': '*' react: 19.2.7 @@ -3078,6 +3569,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-context@1.1.4': + resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-dialog@1.1.15': resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} peerDependencies: @@ -3091,6 +3591,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dialog@1.1.18': + resolution: {integrity: sha512-apa28mldjMgORmE6g/w3sCcA0Y9UAVeeDVoozN4i7kOw12mLl9RBchfzK3Nn6qxOWjrZhK1Lfy7f07kyzxtnBw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-direction@1.1.1': resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: @@ -3100,6 +3613,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-dismissable-layer@1.1.11': resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: @@ -3113,6 +3635,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dismissable-layer@1.1.14': + resolution: {integrity: sha512-4lUhWTWAjbDIqFrAPWJ3WqBOpO5YchVZ88X3nh6H9Lu5AFi5nCUeTPj3D8FSDmabmFeRe9ME0BDA4MwKTha5GQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-dropdown-menu@2.1.16': resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} peerDependencies: @@ -3126,6 +3661,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dropdown-menu@2.1.19': + resolution: {integrity: sha512-HZccBkbK0LOi8nYKIp5jll/zIRW0cCOmG6WWyqsSpmXCU+ZlcBbTqIwlBvPCu886C5RVu6c/kHV7xSP8IgYNHw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-focus-guards@1.1.3': resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} peerDependencies: @@ -3135,6 +3683,28 @@ packages: '@types/react': optional: true + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.11': + resolution: {integrity: sha512-Mn88Vg2whaRocGJNOH+DKFqYm6ySFPQaiwHNxZPyjn99B52KAEJWWY9NP83+nWdk2HM3rdov+STu9AG471Rt9w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-focus-scope@1.1.7': resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} peerDependencies: @@ -3148,6 +3718,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-form@0.1.11': + resolution: {integrity: sha512-0mTMJHv1gQAuEQoq5VDpTD3MRgmfUFdXAVFhpqR7wBeUr+tyRsof0wv/4XdPHLwQrefhoH2FiGHCggrCJhalIw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-form@0.1.8': resolution: {integrity: sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ==} peerDependencies: @@ -3174,6 +3757,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-hover-card@1.1.18': + resolution: {integrity: sha512-rt+Fx4HoCeEwFL2IdoV2QaPltqDLlzxN77i9nwB3Y70scFlfAHh1QCdE2TXKuFJtA1TNygb0oivnFBZifgtZOw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-icons@1.3.2': resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} peerDependencies: @@ -3188,6 +3784,28 @@ packages: '@types/react': optional: true + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-label@2.1.11': + resolution: {integrity: sha512-3PKvDDxOn62k0oV1n4QtNtD2vpu+zYjXR7ojLBPaO6SPvhy53yg0vAmgNeBQeJW5rV3dffoRG+HYfLBZuzw0CQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-label@2.1.7': resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} peerDependencies: @@ -3227,6 +3845,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-menu@2.1.19': + resolution: {integrity: sha512-Mht9BVd1AIsNFVQr4KG3bIK7XQn5IXF0TL/2ObsrzOdc1loaly/+kBDL5roSCYn8j8XZkvpOD0WYLz2FQtH1Eg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-menubar@1.1.16': resolution: {integrity: sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==} peerDependencies: @@ -3240,6 +3871,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-menubar@1.1.19': + resolution: {integrity: sha512-Glt6mebxcgQvLeVkH3HiqV5bgQubE+31ELxLs7q0GlYI5k0XYkOkeuPrhXoylxK8eufvIt9CJjzY1TfFMXK3qw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-navigation-menu@1.2.14': resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==} peerDependencies: @@ -3253,6 +3897,32 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-navigation-menu@1.2.17': + resolution: {integrity: sha512-fYeYQvbeNn5AQk2RBbpO7koLm2YbS00UYxC/IL2sgLlninEH5UNIv+X3E0KJ1Vy4WIo+dhN9w8GNqSHhbHWCIg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-one-time-password-field@0.1.11': + resolution: {integrity: sha512-Rsgab65u73E5kPVh8OS6PgPwJgPyf08GFfJDGAbMdF4DL7CgDhFOaDnXuk/DiMEVF6kgQwl0oJmFklvipmiOLg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-one-time-password-field@0.1.8': resolution: {integrity: sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg==} peerDependencies: @@ -3279,6 +3949,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-password-toggle-field@0.1.6': + resolution: {integrity: sha512-pQ3xGp/uemomASPH97Eb3shfXX8QlG11bBJyEvRBV+vwtO4HvQlS06Yj9f31Ao7XepvF98SFrRgVDQ7jv+2xjQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-popover@1.1.15': resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} peerDependencies: @@ -3292,6 +3975,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popover@1.1.18': + resolution: {integrity: sha512-qdXDes+eHlnMUGlBAAAe5EG7oOQvqsXuq4mq585diMudg80iB+jHbsSeG3+Q4eWNsogNyhqU2p/3i+Y0iEepqg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-popper@1.2.8': resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} peerDependencies: @@ -3305,6 +4001,32 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popper@1.3.2': + resolution: {integrity: sha512-3QXNeMkdshed1MR3LNoiCirBywRFPkD8ETJa/HlPuLwSajaQixf2ro+isoDNJlGABg9ug41XuZpINZJIle4XWg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.13': + resolution: {integrity: sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-portal@1.1.9': resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} peerDependencies: @@ -3331,6 +4053,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-presence@1.1.6': + resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-primitive@2.1.3': resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: @@ -3357,6 +4092,32 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-primitive@2.1.7': + resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-progress@1.1.11': + resolution: {integrity: sha512-KqiGJcFaZDc+BvveAgU3ZhACg2MvSUDrCBx4lRR/ZVRNal0bvt8lBpvnSkep9heeOuF8Qfw3fszLDX4OpQ2NVw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-progress@1.1.7': resolution: {integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==} peerDependencies: @@ -3383,6 +4144,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-radio-group@1.4.2': + resolution: {integrity: sha512-W8Uo9riHnlzLLWy+r2mVHUyuEWqD/+be4PZzbEvaGoFSBDHkm+GYWjtcE6u3AmPKNyfanWpnVfpZ2GqPCdzzsw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-roving-focus@1.1.11': resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} peerDependencies: @@ -3396,6 +4170,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-roving-focus@1.1.14': + resolution: {integrity: sha512-8Qcnx9447tx/aCBgw6Jenfqg4Skq+vqab9mCBmuGNipIS5YXvL275wbKEu7+ICYHIlAPgCduUMJH1XOYewKF6Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-scroll-area@1.2.10': resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} peerDependencies: @@ -3409,6 +4196,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-scroll-area@1.2.13': + resolution: {integrity: sha512-7tncSubo2G0UY1e8rk+72qe3XRzrGnOLtZQ1PL1KoBfRUNX0NrJT5akb+0kfwSCc3gVR4wdHqyhAQBDpDNOwDw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-select@2.2.6': resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} peerDependencies: @@ -3422,6 +4222,32 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-select@2.3.2': + resolution: {integrity: sha512-brXD6C/V0fVK0DDbscLVw6LsXrjQ+ay8jdOBaN+tLb4vsHsAMm6Gt6eT77wHX1Eq8GPtD5rJ+RxFtfDozsb4+Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-separator@1.1.11': + resolution: {integrity: sha512-jRhe86+8PF7VZ1u14eOWVOuh2BuAhALg/FT1VcMC4OHedMTRUazDnDlKTt+yxo5cRNKHMfmvZ4sSQtWDeMV4CQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-separator@1.1.7': resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} peerDependencies: @@ -3461,6 +4287,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-slider@1.4.2': + resolution: {integrity: sha512-qt5C1ppJz66aUDrH1VccjPrq7aFchK0wBrn6xsxlCHNUyE57dRRQ7lp1QFpF7OscMexZF8MCGBTVBlENHPkNiA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.2.3': resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: @@ -3479,8 +4318,108 @@ packages: '@types/react': optional: true - '@radix-ui/react-switch@1.2.6': - resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} + '@radix-ui/react-slot@1.3.0': + resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.2.6': + resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-switch@1.3.2': + resolution: {integrity: sha512-tgRBI3DdNwAJYE4BBZyZcz/HRRCvAsPkRvG1wvKc+41tBGMxPn/a87T/wikXAvyDypNQ9kaZwHbeZe+veHCGpA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tabs@1.1.16': + resolution: {integrity: sha512-v3Ab2l7z6U7tRB4xA0IyKdq0OsqaO1o9ZjsIEoKKnSZ/l96mZz8aCTX0NCXw+YVHJXr8Km4d+Mn6/Q8YjXa+gw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toast@1.2.15': + resolution: {integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toast@1.2.18': + resolution: {integrity: sha512-YNEnTHV47hPep+U0QvVM02OJNka9uygREc+k4Nh5VSZBg4MmE+myI442x3hCGfRpX7N2WSSYSJKws4gE+Z8lgg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle-group@1.1.11': + resolution: {integrity: sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle-group@1.1.14': + resolution: {integrity: sha512-TK1vusNKb8IRhF23FTbRgUNZ9zfs5rGIyI7LfR3h26p9LrQ060i0uW9QWeD8baZMddaaP0DBGlIa6pbZG+mitg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3492,8 +4431,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tabs@1.1.13': - resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + '@radix-ui/react-toggle@1.1.10': + resolution: {integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3505,8 +4444,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toast@1.2.15': - resolution: {integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==} + '@radix-ui/react-toggle@1.1.13': + resolution: {integrity: sha512-bI2ILJrzwgmAsH05TsJ9pVrzqQwAip7OM2/krqAdYn0R16bl86UPWbe5VPHsALat0EnqpV01cGtkleaUKPNdNg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3518,8 +4457,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toggle-group@1.1.11': - resolution: {integrity: sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==} + '@radix-ui/react-toolbar@1.1.11': + resolution: {integrity: sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3531,8 +4470,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toggle@1.1.10': - resolution: {integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==} + '@radix-ui/react-toolbar@1.1.14': + resolution: {integrity: sha512-L/EkWVqlnj3lL2toHh4C7PwH2jxfa7OCq6lGfXSCii99ve2S4Ux5rc9HnOa7LN9exHa/Nl9kmCAmP9BuDPy5UA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3544,8 +4483,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toolbar@1.1.11': - resolution: {integrity: sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==} + '@radix-ui/react-tooltip@1.2.11': + resolution: {integrity: sha512-8XZ6Py3y3W2nEzAUGCN5cfVKaUi+CVApcz1d6lrNVVf2hvYEixMRkq8k9ggPKnQUpRRuOV5avt8uvxViH2jLwA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3579,6 +4518,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-controllable-state@1.2.2': resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} peerDependencies: @@ -3588,6 +4536,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-effect-event@0.0.2': resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: @@ -3597,6 +4554,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-escape-keydown@1.1.1': resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} peerDependencies: @@ -3606,6 +4572,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-escape-keydown@1.1.3': + resolution: {integrity: sha512-3wEkMiPHXha/2VadZ68rYBcmYnPINVGl4Y3gtcM7fKRjANk0OscK+cdqBgUWdozb7YJxsh0vefM7vgAMHXOjqg==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-is-hydrated@0.1.0': resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} peerDependencies: @@ -3615,6 +4590,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-is-hydrated@0.1.1': + resolution: {integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-layout-effect@1.1.1': resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: @@ -3624,6 +4608,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-previous@1.1.1': resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} peerDependencies: @@ -3633,6 +4626,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-previous@1.1.2': + resolution: {integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-rect@1.1.1': resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: @@ -3642,6 +4644,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-size@1.1.1': resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} peerDependencies: @@ -3651,6 +4662,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-visually-hidden@1.2.3': resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} peerDependencies: @@ -3664,9 +4684,25 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-visually-hidden@1.2.7': + resolution: {integrity: sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} + '@remirror/core-constants@3.0.0': resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} @@ -3768,6 +4804,9 @@ packages: '@rolldown/pluginutils@1.0.0-beta.40': resolution: {integrity: sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w==} + '@rolldown/pluginutils@1.0.0-rc.3': + resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} + '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} @@ -4202,6 +5241,36 @@ packages: resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} engines: {node: '>=18.0.0'} + '@solid-primitives/event-listener@2.4.5': + resolution: {integrity: sha512-nwRV558mIabl4yVAhZKY8cb6G+O1F0M6Z75ttTu5hk+SxdOnKSGj+eetDIu7Oax1P138ZdUU01qnBPR8rnxaEA==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/keyboard@1.3.5': + resolution: {integrity: sha512-sav+l+PL+74z3yaftVs7qd8c2SXkqzuxPOVibUe5wYMt+U5Hxp3V3XCPgBPN2I6cANjvoFtz0NiU8uHVLdi9FQ==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/resize-observer@2.1.5': + resolution: {integrity: sha512-AiyTknKcNBaKHbcSMuxtSNM8FjIuiSuFyFghdD0TcCMU9hKi9EmsC5pjfjDwxE+5EueB1a+T/34PLRI5vbBbKw==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/rootless@1.5.3': + resolution: {integrity: sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/static-store@0.1.3': + resolution: {integrity: sha512-uxez7SXnr5GiRnzqO2IEDjOJRIXaG+0LZLBizmUA1FwSi+hrpuMzVBwyk70m4prcl8X6FDDXUl9O8hSq8wHbBQ==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/utils@6.4.0': + resolution: {integrity: sha512-AeGTBg8Wtkh/0s+evyLtP8piQoS4wyqqQaAFs2HJcFMMjYAtUgo+ZPduRXLjPlqKVc2ejeR544oeqpbn8Egn8A==} + peerDependencies: + solid-js: ^1.6.12 + '@stackblitz/sdk@1.11.0': resolution: {integrity: sha512-DFQGANNkEZRzFk1/rDP6TcFdM82ycHE+zfl9C/M/jXlH68jiqHWHFMQURLELoD8koxvu/eW5uhg94NSAZlYrUQ==} @@ -4214,42 +5283,81 @@ packages: '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@stylistic/eslint-plugin@5.10.0': + resolution: {integrity: sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^9.0.0 || ^10.0.0 + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} '@tailwindcss/node@4.2.2': resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} + '@tailwindcss/node@4.3.2': + resolution: {integrity: sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==} + '@tailwindcss/oxide-android-arm64@4.2.2': resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} engines: {node: '>= 20'} cpu: [arm64] os: [android] + '@tailwindcss/oxide-android-arm64@4.3.2': + resolution: {integrity: sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + '@tailwindcss/oxide-darwin-arm64@4.2.2': resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] + '@tailwindcss/oxide-darwin-arm64@4.3.2': + resolution: {integrity: sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + '@tailwindcss/oxide-darwin-x64@4.2.2': resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] + '@tailwindcss/oxide-darwin-x64@4.3.2': + resolution: {integrity: sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + '@tailwindcss/oxide-freebsd-x64@4.2.2': resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] + '@tailwindcss/oxide-freebsd-x64@4.3.2': + resolution: {integrity: sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} engines: {node: '>= 20'} cpu: [arm] os: [linux] + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': + resolution: {integrity: sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} engines: {node: '>= 20'} @@ -4257,6 +5365,13 @@ packages: os: [linux] libc: [glibc] + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': + resolution: {integrity: sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} engines: {node: '>= 20'} @@ -4264,6 +5379,13 @@ packages: os: [linux] libc: [musl] + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': + resolution: {integrity: sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} engines: {node: '>= 20'} @@ -4271,6 +5393,13 @@ packages: os: [linux] libc: [glibc] + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': + resolution: {integrity: sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + '@tailwindcss/oxide-linux-x64-musl@4.2.2': resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} engines: {node: '>= 20'} @@ -4278,6 +5407,13 @@ packages: os: [linux] libc: [musl] + '@tailwindcss/oxide-linux-x64-musl@4.3.2': + resolution: {integrity: sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + '@tailwindcss/oxide-wasm32-wasi@4.2.2': resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} engines: {node: '>=14.0.0'} @@ -4290,22 +5426,50 @@ packages: - '@emnapi/wasi-threads' - tslib + '@tailwindcss/oxide-wasm32-wasi@4.3.2': + resolution: {integrity: sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': + resolution: {integrity: sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} engines: {node: '>= 20'} cpu: [x64] os: [win32] + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': + resolution: {integrity: sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + '@tailwindcss/oxide@4.2.2': resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} engines: {node: '>= 20'} + '@tailwindcss/oxide@4.3.2': + resolution: {integrity: sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==} + engines: {node: '>= 20'} + '@tailwindcss/postcss@4.2.2': resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==} @@ -4314,6 +5478,50 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + '@tailwindcss/vite@4.3.2': + resolution: {integrity: sha512-eHpMeX4JXfVNJDEcsouTeCBubJBTcTLigeaw/NTUW6PB5ATKKXdyonnXgTBX2VuRbjz1hjfz6C5XAhr52ImQXA==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@tanstack/devtools-client@0.0.8': + resolution: {integrity: sha512-cG3iZkGWCwN330bLBKa8+9r4Of2AXNoz2zUqcsy/4XsD3105ghVBx78cGyvJj9fSclNomPxoqAnDGXXhg1WLvA==} + engines: {node: '>=18'} + + '@tanstack/devtools-event-bus@0.4.2': + resolution: {integrity: sha512-2LHzhwBFlKHCcklsQrGe8TeyjHd4XAF8nuCO6wHmva5fePUkJUULbu6CsCNAlGlCi0KkEsMXZSvRdR4HgMq4yA==} + engines: {node: '>=18'} + + '@tanstack/devtools-event-client@0.5.0': + resolution: {integrity: sha512-H+OH3zC6Vhu/K0NaVfQKknEKawc/+2PT+D3SB3Ox0V8SiMlTo0abbmH2rH0721R2aNYbjdMXA1oENOd8E2UVoA==} + engines: {node: '>=18'} + hasBin: true + + '@tanstack/devtools-ui@0.6.0': + resolution: {integrity: sha512-CVaM6rT6Nl5ijo83vJYFa2SjofvpuOl/uOvbYGhBrRgUhhelNHhx8zZX+hnZCHmIr0/lzM65hsocnZ72592Rvg==} + engines: {node: '>=18'} + peerDependencies: + solid-js: '>=1.9.7' + + '@tanstack/devtools-vite@0.8.1': + resolution: {integrity: sha512-oQxOo0fI0bwhHtw/psFlIR0OS/bsKrirBxwnw2vuhCM4bjt3k4EZZsW/lvZ1+Vpouhts7LSyvngnxvGXbQ1sUQ==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@tanstack/devtools@0.12.5': + resolution: {integrity: sha512-JdxTSeVdjJheycgz4c7qbldNKDCEDWlWr1l9dZBhd9sOmRBT5Z70ka9Eb8mb+FUnalcOIB62IDSR/iSxAIUD8Q==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + solid-js: '>=1.9.7' + + '@tanstack/eslint-config@0.4.0': + resolution: {integrity: sha512-V+Cd81W/f65dqKJKpytbwTGx9R+IwxKAHsG/uJ3nSLYEh36hlAr54lRpstUhggQB8nf/cP733cIw8DuD2dzQUg==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^9.0.0 || ^10.0.0 + '@tanstack/history@1.161.6': resolution: {integrity: sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg==} engines: {node: '>=20.19'} @@ -4321,11 +5529,51 @@ packages: '@tanstack/query-core@5.90.10': resolution: {integrity: sha512-EhZVFu9rl7GfRNuJLJ3Y7wtbTnENsvzp+YpcAV7kCYiXni1v8qZh++lpw4ch4rrwC0u/EZRnBHIehzCGzwXDSQ==} + '@tanstack/query-devtools@5.101.2': + resolution: {integrity: sha512-o+wHcqgN7Pp0s8v1i0UGq/ZrrEKrxdIiMQmKRdYb2w7NPtylYSJ4+wg/tIn71m9DLstwUwdEGAvROdly6HXP6w==} + + '@tanstack/react-devtools@0.10.8': + resolution: {integrity: sha512-YJV6YttQf9lhhPbPBLULgy1eScEvJUMsCS26mjg9hfBKgAJQA5sF9zvzorDzW9Ob6o/asoXikO81JnRUVuFX0Q==} + engines: {node: '>=18'} + peerDependencies: + '@types/react': '>=16.8' + '@types/react-dom': '>=16.8' + react: 19.2.7 + react-dom: 19.2.7 + + '@tanstack/react-query-devtools@5.101.2': + resolution: {integrity: sha512-eU7HctdA9gDjqoERoEdzLbw9DiqnBDfh5+Hu0u26gjqoHJezOpQAuiesDL2VvkU+2cPV76zgv0tMZsOrI4LjnQ==} + peerDependencies: + '@tanstack/react-query': ^5.90.2 + react: 19.2.7 + '@tanstack/react-query@5.90.10': resolution: {integrity: sha512-BKLss9Y8PQ9IUjPYQiv3/Zmlx92uxffUOX8ZZNoQlCIZBJPT5M+GOMQj7xislvVQ6l1BstBjcX0XB/aHfFYVNw==} peerDependencies: react: 19.2.7 + '@tanstack/react-router-devtools@1.167.0': + resolution: {integrity: sha512-nGw095EG7IHx0h5NtlEmzf6vcCTaFNPWdTSuDKazajhN0ct/v/TkekJ9J6KYUCeV1a8/2ZmToc58M+0rrOyn7w==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/react-router': ^1.170.0 + '@tanstack/router-core': ^1.170.0 + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@tanstack/router-core': + optional: true + + '@tanstack/react-router-ssr-query@1.167.1': + resolution: {integrity: sha512-W9j5JPnBikyafvuUfykFfHIWod58OAbAAa5leNkXBcoDoocghMmu6w9uZOmUZvAWT7CSvgj5tBUtF7CM2OoHXQ==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/query-core': '>=5.90.0' + '@tanstack/react-query': ^5.90.2 + '@tanstack/react-router': '>=1.127.0' + react: 19.2.7 + react-dom: 19.2.7 + '@tanstack/react-router@1.168.10': resolution: {integrity: sha512-/RmDlOwDkCug609KdPB3U+U1zmrtadJpvsmRg2zEn8TRCKRNri7dYZIjQZbNg8PgUiRL4T6njrZBV1ChzblNaA==} engines: {node: '>=20.19'} @@ -4367,6 +5615,16 @@ packages: engines: {node: '>=20.19'} hasBin: true + '@tanstack/router-devtools-core@1.168.0': + resolution: {integrity: sha512-wQoQhlBK7nlZgqzaqdYXKWNTpdHdsaREdaPhFZVH0/Ador+F+eM3/NF2i3f2LPeS0GgKraZUQXe1Q/1+KHyEYg==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/router-core': ^1.170.0 + csstype: ^3.0.10 + peerDependenciesMeta: + csstype: + optional: true + '@tanstack/router-generator@1.166.24': resolution: {integrity: sha512-vdaGKwuH+r+DPe6R1mjk+TDDmDH6NTG7QqwxHqGEvOH4aGf9sPjhmRKNJZqQr8cPIbfp6u5lXyZ1TeDcSNMVEA==} engines: {node: '>=20.19'} @@ -4393,6 +5651,13 @@ packages: webpack: optional: true + '@tanstack/router-ssr-query-core@1.169.1': + resolution: {integrity: sha512-rngux8s/3mPQzcjLYDLkNU31coYVyCgrVTfpdwqUdY5jIEHqGTXrO73DTkPR1PppwYUeVhmNCgl8TctRcnupjg==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/query-core': '>=5.90.0' + '@tanstack/router-core': '>=1.127.0' + '@tanstack/router-utils@1.161.6': resolution: {integrity: sha512-nRcYw+w2OEgK6VfjirYvGyPLOK+tZQz1jkYcmH5AjMamQ9PycnlxZF2aEZtPpNoUsaceX2bHptn6Ub5hGXqNvw==} engines: {node: '>=20.19'} @@ -4433,6 +5698,25 @@ packages: engines: {node: '>=20.19'} hasBin: true + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@tiptap/core@3.20.0': resolution: {integrity: sha512-aC9aROgia/SpJqhsXFiX9TsligL8d+oeoI8W3u00WI45s0VfsqjgeKQLDLF7Tu7hC+7F02teC84SAHuup003VQ==} peerDependencies: @@ -4658,6 +5942,21 @@ packages: '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@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/bun@1.3.2': resolution: {integrity: sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg==} @@ -4674,6 +5973,9 @@ packages: resolution: {integrity: sha512-o7jqJM04gfaYrdCecCVMbZhNdG6T1MHg/oQoRFdERLV+4d+V7FijhiEAbFu0Usww84Yijk9yH58U4Jk4HbtzZw==} deprecated: This is a stub types definition. diff provides its own type definitions, so you do not need this installed. + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -4689,6 +5991,9 @@ packages: '@types/inquirer@6.5.0': resolution: {integrity: sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} @@ -4726,6 +6031,9 @@ packages: '@types/node@20.19.25': resolution: {integrity: sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==} + '@types/node@22.20.0': + resolution: {integrity: sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==} + '@types/node@24.0.3': resolution: {integrity: sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==} @@ -4788,11 +6096,19 @@ packages: '@types/whatwg-url@11.0.5': resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} - '@typescript-eslint/eslint-plugin@8.58.0': - resolution: {integrity: sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==} + '@typescript-eslint/eslint-plugin@8.58.0': + resolution: {integrity: sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.58.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/eslint-plugin@8.62.1': + resolution: {integrity: sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.58.0 + '@typescript-eslint/parser': ^8.62.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' @@ -4803,14 +6119,21 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/parser@8.62.1': + resolution: {integrity: sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/project-service@8.58.0': resolution: {integrity: sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.61.0': - resolution: {integrity: sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==} + '@typescript-eslint/project-service@8.62.1': + resolution: {integrity: sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -4819,8 +6142,8 @@ packages: resolution: {integrity: sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.61.0': - resolution: {integrity: sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==} + '@typescript-eslint/scope-manager@8.62.1': + resolution: {integrity: sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.58.0': @@ -4829,8 +6152,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/tsconfig-utils@8.61.0': - resolution: {integrity: sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==} + '@typescript-eslint/tsconfig-utils@8.62.1': + resolution: {integrity: sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -4842,6 +6165,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@8.62.1': + resolution: {integrity: sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/types@8.58.0': resolution: {integrity: sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4850,14 +6180,18 @@ packages: resolution: {integrity: sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.62.1': + resolution: {integrity: sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.58.0': resolution: {integrity: sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/typescript-estree@8.61.0': - resolution: {integrity: sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==} + '@typescript-eslint/typescript-estree@8.62.1': + resolution: {integrity: sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -4869,8 +6203,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.61.0': - resolution: {integrity: sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==} + '@typescript-eslint/utils@8.62.1': + resolution: {integrity: sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -4880,8 +6214,8 @@ packages: resolution: {integrity: sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.61.0': - resolution: {integrity: sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==} + '@typescript-eslint/visitor-keys@8.62.1': + resolution: {integrity: sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -5152,9 +6486,18 @@ packages: resolution: {integrity: sha512-yNEQvPcVrK9sIe637+I0jD6leluPxzwJKx/Haw6F4H77CdDsszUn5V3o96LPziXkSNE2B83+Z3mjqGKBK/R6Gg==} engines: {node: '>= 20'} + '@vitejs/plugin-react@5.2.0': + resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/expect@4.1.9': + resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} + '@vitest/mocker@3.2.4': resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: @@ -5166,21 +6509,47 @@ packages: vite: optional: true + '@vitest/mocker@4.1.9': + resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + '@vitest/pretty-format@4.1.9': + resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} + '@vitest/runner@3.2.4': resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + '@vitest/runner@4.1.9': + resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} + '@vitest/snapshot@3.2.4': resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + '@vitest/snapshot@4.1.9': + resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} + '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/spy@4.1.9': + resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} + '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@4.1.9': + resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} + '@vue/compiler-core@3.5.24': resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==} @@ -5281,6 +6650,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + ansis@4.2.0: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} @@ -5306,6 +6679,9 @@ packages: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -5618,6 +6994,10 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -5858,6 +7238,14 @@ packages: srvx: optional: true + crossws@0.4.9: + resolution: {integrity: sha512-iWx+1OMSG2aOHpjyf9AESOzkwsVdS49cXM9dVrI2PDhxU5l2RIWE/KG56gk4BbAnsMoycvniJ9OnOxO9LRzHVA==} + peerDependencies: + srvx: '>=0.11.5' + peerDependenciesMeta: + srvx: + optional: true + css-declaration-sorter@7.3.0: resolution: {integrity: sha512-LQF6N/3vkAMYF4xoHLJfG718HRJh34Z8BnNhd6bosOMIVjMlhuZK5++oZa3uYAgrI5+7x2o27gUqTR2U/KjUOQ==} engines: {node: ^14 || ^16 || >=18} @@ -5946,6 +7334,32 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} + + db0@0.3.4: + resolution: {integrity: sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==} + peerDependencies: + '@electric-sql/pglite': '*' + '@libsql/client': '*' + better-sqlite3: '*' + drizzle-orm: '*' + mysql2: '*' + sqlite3: '*' + peerDependenciesMeta: + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + better-sqlite3: + optional: true + drizzle-orm: + optional: true + mysql2: + optional: true + sqlite3: + optional: true + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -6086,6 +7500,9 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -6170,6 +7587,10 @@ packages: resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} + enhanced-resolve@5.21.6: + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + engines: {node: '>=10.13.0'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -6190,6 +7611,24 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} + env-runner@0.1.16: + resolution: {integrity: sha512-2LRJM4P2KLX6J83QZZrMqvgCDt/D5ea7wPcI3yYiy5cG/9rX5QwdwZFx0D7ktWnjdRyZxYjttGGorb5nFqb1CA==} + hasBin: true + peerDependencies: + '@netlify/runtime': ^4.1.23 + '@vercel/queue': '>=0.2.0' + miniflare: ^4.20260515.0 + wrangler: ^4.0.0 + peerDependenciesMeta: + '@netlify/runtime': + optional: true + '@vercel/queue': + optional: true + miniflare: + optional: true + wrangler: + optional: true + error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -6212,6 +7651,9 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.3.0: + resolution: {integrity: sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -6273,6 +7715,12 @@ packages: engines: {node: '>=6.0'} hasBin: true + eslint-compat-utils@0.5.1: + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + eslint-config-next@15.3.4: resolution: {integrity: sha512-WqeumCq57QcTP2lYlV6BRUySfGiBYEXlQ1L0mQ+u4N4X4ZhUVSSQ52WtjqHv60pJ6dD7jn+YZc0d1/ZSsxccvg==} peerDependencies: @@ -6328,6 +7776,12 @@ packages: eslint-import-resolver-webpack: optional: true + eslint-plugin-es-x@7.8.0: + resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + eslint-plugin-import-x@4.16.2: resolution: {integrity: sha512-rM9K8UBHcWKpzQzStn1YRN2T5NvdeIfSVoKu/lKF41znQXHAUcBbYXe5wd6GNjZjTrP7viQ49n1D83x/2gYgIw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6357,6 +7811,12 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + eslint-plugin-n@17.24.0: + resolution: {integrity: sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.23.0' + eslint-plugin-react-hooks@5.2.0: resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} engines: {node: '>=10'} @@ -6373,10 +7833,22 @@ packages: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@5.0.1: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -6387,6 +7859,24 @@ packages: deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6463,6 +7953,10 @@ packages: resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + express-rate-limit@8.3.2: resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} engines: {node: '>= 16'} @@ -6476,6 +7970,9 @@ packages: exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + exsolve@1.1.0: + resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -6552,6 +8049,10 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -6571,6 +8072,10 @@ packages: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} @@ -6828,6 +8333,18 @@ packages: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + globals@17.7.0: + resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} + engines: {node: '>=18'} + globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -6836,6 +8353,14 @@ packages: resolution: {integrity: sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==} engines: {node: '>=8'} + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + goober@2.1.19: + resolution: {integrity: sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg==} + peerDependencies: + csstype: ^3.0.10 + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -6870,6 +8395,16 @@ packages: crossws: optional: true + h3@2.0.1-rc.22: + resolution: {integrity: sha512-Esv0DMIuPkCTSWCA0vO73vcTqwzH1wjSrAO1TXNu/K3up1sZHa9EKMapbmxCDYBeymC3fVTk4qxp7ogQWQ+KgA==} + engines: {node: '>=20.11.1'} + hasBin: true + peerDependencies: + crossws: ^0.4.1 + peerDependenciesMeta: + crossws: + optional: true + handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -6983,6 +8518,9 @@ packages: hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -7011,6 +8549,9 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + httpxy@0.5.4: + resolution: {integrity: sha512-URfeibL0kTH6VuIxxaJDXWQWEk8fKr+9L8MGv6CuAiNy0fGnoVhWbXBvJR1mkdsvCDUxvhX9cW60k2AhtH5s6w==} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -7354,6 +8895,10 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + jose@6.2.0: resolution: {integrity: sha512-xsfE1TcSCbUdo6U07tR0mvhg0flGxU8tPLbF03mirl2ukGQENhUg4ubGYQnhVH0b5stLlPM+WOqDkEl1R1y5sQ==} @@ -7462,6 +9007,9 @@ packages: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} + launch-editor@2.14.1: + resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -7646,11 +9194,20 @@ packages: peerDependencies: react: 19.2.7 + lucide-react@0.545.0: + resolution: {integrity: sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw==} + peerDependencies: + react: 19.2.7 + lucide-react@1.7.0: resolution: {integrity: sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==} peerDependencies: react: 19.2.7 + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -8073,6 +9630,40 @@ packages: sass: optional: true + nf3@0.3.19: + resolution: {integrity: sha512-tfOXX/ivQBL+4km/fzxQ0HWMmp1Ewx/YpFLbya080gXVfm26bZy0n4a5K5PPnqTLyuAHu0FobVpXXUadIiIwIQ==} + + nitro@3.0.260603-beta: + resolution: {integrity: sha512-ffaSHK00a7YDlDizoEHwcxPwpQpdBRRA8k42ymTsRnfl3ipGeKgv4gnPr6DgmCNTo4tYVPK3bHBEv1gNhWpo/A==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@vercel/queue': ^0.2.0 + dotenv: '*' + giget: '*' + jiti: ^2.7.0 + rollup: ^4.60.4 + vite: ^7 || ^8 + xml2js: ^0.6.2 + zephyr-agent: ^0.2.0 + peerDependenciesMeta: + '@vercel/queue': + optional: true + dotenv: + optional: true + giget: + optional: true + jiti: + optional: true + rollup: + optional: true + vite: + optional: true + xml2js: + optional: true + zephyr-agent: + optional: true + no-case@2.3.2: resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} @@ -8188,6 +9779,16 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + engines: {node: '>=12.20.0'} + + ocache@0.1.5: + resolution: {integrity: sha512-kNNnkkVQup/QDvmTz8Q84wc2ntiyoVHDxa6eHWKt5qdGAmFRBIxy83rxgCYEjW0x06UJ9E3P6VgM2yY4rOBH4w==} + + ofetch@2.0.0-alpha.3: + resolution: {integrity: sha512-zpYTCs2byOuft65vI3z43Dd6iSdFbOZZLb9/d21aCpx2rGastVU9dOCv0lu4ykc1Ur1anAYjDi3SUvR0vq50JA==} + ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} @@ -8250,6 +9851,10 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + oxc-parser@0.120.0: + resolution: {integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==} + engines: {node: ^20.19.0 || >=22.12.0} + oxc-resolver@11.19.1: resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==} @@ -8645,6 +10250,61 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier-plugin-tailwindcss@0.8.0: + resolution: {integrity: sha512-V8ITGH87yuBDF6JpEZTOVlUz/saAwqb8f3HRgUj8Lh+tGCcrmorhsLpYqzygwFwK0PE2Ib6Mv3M7T/uE2tZV1g==} + engines: {node: '>=20.19'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-hermes': '*' + '@prettier/plugin-oxc': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-hermes': + optional: true + '@prettier/plugin-oxc': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-svelte: + optional: true + prettier@3.8.4: resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==} engines: {node: '>=14'} @@ -8654,6 +10314,10 @@ packages: resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} engines: {node: '>=20'} + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + pretty-hrtime@1.0.3: resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} engines: {node: '>= 0.8'} @@ -8815,6 +10479,19 @@ packages: '@types/react-dom': optional: true + radix-ui@1.6.1: + resolution: {integrity: sha512-QXDXJtB6sK83mLASONYUZCauatcWb+knFviFpN1EhtdbbmlsRmzCLrbZSKztnNiem2KOHIBbiDbauVB7SORXMw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 19.2.7 + react-dom: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -8866,6 +10543,9 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-markdown@9.1.0: resolution: {integrity: sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==} peerDependencies: @@ -8878,6 +10558,10 @@ packages: react: 19.2.7 react-dom: 19.2.7 + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -8898,6 +10582,16 @@ packages: '@types/react': optional: true + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: 19.2.7 + peerDependenciesMeta: + '@types/react': + optional: true + react-resizable-panels@2.1.9: resolution: {integrity: sha512-z77+X08YDIrgAes4jl8xhnUu1LNIRp4+E7cv4xHmLOxxUPO/ML7PSrE813b90vj7xvQ1lcf7g2uA9GeMZonjhQ==} peerDependencies: @@ -9274,6 +10968,11 @@ packages: resolution: {integrity: sha512-qNQcCavkbYsgBj+X09tF2bTcwRd8abR880bsFkDU2kMqceMCLAm5c+cLg7kWDhfh1H9g08knpQ5ZEf6y/co16g==} hasBin: true + shadcn@4.13.0: + resolution: {integrity: sha512-5fuJ4jI/GcPeA/iTL4cJivCZuYQGXz/N3bIzyd+Gd/FM6xUCy2MxGG+LaDQuw2cjNy9zGPSFPTEmI048UwPTZA==} + engines: {node: '>=20.18.1'} + hasBin: true + sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -9286,6 +10985,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shell-quote@1.9.0: + resolution: {integrity: sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==} + engines: {node: '>= 0.4'} + shiki@3.15.0: resolution: {integrity: sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw==} @@ -9385,6 +11088,11 @@ packages: engines: {node: '>=20.16.0'} hasBin: true + srvx@0.11.20: + resolution: {integrity: sha512-gdPvbwpJOJpMyBYcp39q78C4pmv/Aw5WwZKB3+/pfeUVxo3EvNXlOi1fxzoy2ECGvUeBhouXy9rBxstjQQOPog==} + engines: {node: '>=20.16.0'} + hasBin: true + stable-hash-x@0.2.0: resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} engines: {node: '>=12.0.0'} @@ -9402,6 +11110,9 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + stdin-discarder@0.2.2: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} @@ -9564,10 +11275,17 @@ packages: tailwindcss@4.2.2: resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} + tailwindcss@4.3.2: + resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==} + tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -9616,6 +11334,10 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + tinyspy@4.0.4: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} @@ -9666,6 +11388,11 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-declaration-location@1.0.7: + resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} + peerDependencies: + typescript: '>=4.0.0' + ts-morph@26.0.0: resolution: {integrity: sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==} @@ -9686,6 +11413,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==} @@ -9782,11 +11520,23 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} + typescript-eslint@8.62.1: + resolution: {integrity: sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} @@ -9838,6 +11588,9 @@ packages: resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==} engines: {node: '>=20.18.1'} + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} @@ -9887,6 +11640,80 @@ packages: unrs-resolver@1.12.2: resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} + unstorage@2.0.0-alpha.7: + resolution: {integrity: sha512-ELPztchk2zgFJnakyodVY3vJWGW9jy//keJ32IOJVGUMyaPydwcA1FtVvWqT0TNRch9H+cMNEGllfVFfScImog==} + peerDependencies: + '@azure/app-configuration': ^1.11.0 + '@azure/cosmos': ^4.9.1 + '@azure/data-tables': ^13.3.2 + '@azure/identity': ^4.13.0 + '@azure/keyvault-secrets': ^4.10.0 + '@azure/storage-blob': ^12.31.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.13.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.36.2 + '@vercel/blob': '>=0.27.3' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1.0.1 + aws4fetch: ^1.0.20 + chokidar: ^4 || ^5 + db0: '>=0.3.4' + idb-keyval: ^6.2.2 + ioredis: ^5.9.3 + lru-cache: ^11.2.6 + mongodb: ^6 || ^7 + ofetch: '*' + uploadthing: ^7.7.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + chokidar: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + lru-cache: + optional: true + mongodb: + optional: true + ofetch: + optional: true + uploadthing: + optional: true + until-async@3.0.2: resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} @@ -9994,6 +11821,14 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + vite@7.3.1: resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -10070,6 +11905,53 @@ packages: jsdom: optional: true + vitest@4.1.9: + resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.9 + '@vitest/browser-preview': 4.1.9 + '@vitest/browser-webdriverio': 4.1.9 + '@vitest/coverage-istanbul': 4.1.9 + '@vitest/coverage-v8': 4.1.9 + '@vitest/ui': 4.1.9 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vue-eslint-parser@10.4.1: + resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + vue@3.5.24: resolution: {integrity: sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==} peerDependencies: @@ -10180,6 +12062,18 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + wsl-utils@0.3.1: resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} engines: {node: '>=20'} @@ -10270,8 +12164,7 @@ packages: snapshots: - '@acemir/cssom@0.9.31': - optional: true + '@acemir/cssom@0.9.31': {} '@ai-sdk/gateway@2.0.10(zod@4.4.3)': dependencies: @@ -10280,6 +12173,12 @@ snapshots: '@vercel/oidc': 3.0.3 zod: 4.4.3 + '@ai-sdk/openai@2.0.110(zod@4.4.3)': + dependencies: + '@ai-sdk/provider': 2.0.3 + '@ai-sdk/provider-utils': 3.0.28(zod@4.4.3) + zod: 4.4.3 + '@ai-sdk/provider-utils@3.0.17(zod@4.4.3)': dependencies: '@ai-sdk/provider': 2.0.0 @@ -10287,10 +12186,21 @@ snapshots: eventsource-parser: 3.0.6 zod: 4.4.3 + '@ai-sdk/provider-utils@3.0.28(zod@4.4.3)': + dependencies: + '@ai-sdk/provider': 2.0.3 + '@standard-schema/spec': 1.1.0 + eventsource-parser: 3.0.6 + zod: 4.4.3 + '@ai-sdk/provider@2.0.0': dependencies: json-schema: 0.4.0 + '@ai-sdk/provider@2.0.3': + dependencies: + json-schema: 0.4.0 + '@ai-sdk/react@2.0.94(react@19.2.7)(zod@4.4.3)': dependencies: '@ai-sdk/provider-utils': 3.0.17(zod@4.4.3) @@ -10310,7 +12220,6 @@ snapshots: '@csstools/css-color-parser': 4.1.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - optional: true '@asamuzakjp/dom-selector@6.8.1': dependencies: @@ -10319,13 +12228,10 @@ snapshots: css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 lru-cache: 11.5.1 - optional: true - '@asamuzakjp/generational-cache@1.0.1': - optional: true + '@asamuzakjp/generational-cache@1.0.1': {} - '@asamuzakjp/nwsapi@2.3.9': - optional: true + '@asamuzakjp/nwsapi@2.3.9': {} '@aws-crypto/crc32@5.2.0': dependencies: @@ -10794,7 +12700,6 @@ snapshots: '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - optional: true '@babel/code-frame@7.29.0': dependencies: @@ -10811,8 +12716,7 @@ snapshots: '@babel/compat-data@7.29.0': {} - '@babel/compat-data@7.29.7': - optional: true + '@babel/compat-data@7.29.7': {} '@babel/core@7.29.0': dependencies: @@ -10822,7 +12726,7 @@ snapshots: '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) '@babel/helpers': 7.29.2 '@babel/parser': 7.29.7 - '@babel/template': 7.28.6 + '@babel/template': 7.29.7 '@babel/traverse': 7.29.7 '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 @@ -10853,7 +12757,6 @@ snapshots: semver: 6.3.1 transitivePeerDependencies: - supports-color - optional: true '@babel/generator@7.29.7': dependencies: @@ -10882,7 +12785,6 @@ snapshots: browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 - optional: true '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.29.0)': dependencies: @@ -10897,6 +12799,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.7 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/helper-globals@7.29.7': {} '@babel/helper-member-expression-to-functions@7.28.5': @@ -10946,7 +12861,6 @@ snapshots: '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - optional: true '@babel/helper-optimise-call-expression@7.27.1': dependencies: @@ -10965,6 +12879,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-replace-supers@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: '@babel/traverse': 7.29.7 @@ -10981,8 +12904,7 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helper-validator-option@7.29.7': - optional: true + '@babel/helper-validator-option@7.29.7': {} '@babel/helpers@7.29.2': dependencies: @@ -10993,7 +12915,6 @@ snapshots: dependencies: '@babel/template': 7.29.7 '@babel/types': 7.29.7 - optional: true '@babel/parser@7.29.2': dependencies: @@ -11012,18 +12933,16 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - optional: true - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - optional: true '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.29.0)': dependencies: @@ -11033,6 +12952,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -11040,7 +12977,18 @@ snapshots: '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) transitivePeerDependencies: - supports-color @@ -11055,18 +13003,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/preset-typescript@7.28.5(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + '@babel/runtime-corejs3@7.28.4': dependencies: core-js-pure: 3.47.0 '@babel/runtime@7.29.2': {} - '@babel/template@7.28.6': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 - '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 @@ -11160,6 +13113,14 @@ snapshots: '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3) prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3) + '@better-auth/prisma-adapter@1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(typescript@6.0.3))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))': + dependencies: + '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) + '@better-auth/utils': 0.4.1 + optionalDependencies: + '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(typescript@6.0.3) + prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) + '@better-auth/telemetry@1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)': dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) @@ -11216,13 +13177,79 @@ snapshots: '@bramus/specificity@2.4.2': dependencies: css-tree: 3.2.1 - optional: true - '@btst/adapter-memory@2.2.2(3f1048c33dcc3a34f5463c3b01c66883)': + '@btst/adapter-memory@2.2.2(1cc8778580309809cf6cdfc6c185225b)': + dependencies: + '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) + '@btst/db': 2.2.2(84c0473dacd82373aa0e1c019674ae07) + better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) + transitivePeerDependencies: + - '@better-auth/utils' + - '@better-fetch/fetch' + - '@cloudflare/workers-types' + - '@lynx-js/react' + - '@opentelemetry/api' + - '@prisma/client' + - '@sveltejs/kit' + - '@tanstack/react-start' + - '@tanstack/solid-start' + - better-call + - better-sqlite3 + - drizzle-kit + - drizzle-orm + - jose + - kysely + - mongodb + - mysql2 + - nanostores + - next + - pg + - prisma + - react + - react-dom + - solid-js + - svelte + - vitest + - vue + + '@btst/adapter-memory@2.2.2(e0348a04c834c515f6e6974fb4bbed6d)': + dependencies: + '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) + '@btst/db': 2.2.2(7aa0e8a71fdebdc1d52576bb8fea318f) + better-auth: 1.6.16(fb120b41aed529c3fb40587bdf11e900) + transitivePeerDependencies: + - '@better-auth/utils' + - '@better-fetch/fetch' + - '@cloudflare/workers-types' + - '@lynx-js/react' + - '@opentelemetry/api' + - '@prisma/client' + - '@sveltejs/kit' + - '@tanstack/react-start' + - '@tanstack/solid-start' + - better-call + - better-sqlite3 + - drizzle-kit + - drizzle-orm + - jose + - kysely + - mongodb + - mysql2 + - nanostores + - next + - pg + - prisma + - react + - react-dom + - solid-js + - svelte + - vitest + - vue + + '@btst/db@2.2.2(7aa0e8a71fdebdc1d52576bb8fea318f)': dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - '@btst/db': 2.2.2(7010515aebf7f15e3f6fd94d16578921) - better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) + better-auth: 1.6.16(fb120b41aed529c3fb40587bdf11e900) transitivePeerDependencies: - '@better-auth/utils' - '@better-fetch/fetch' @@ -11252,10 +13279,10 @@ snapshots: - vitest - vue - '@btst/db@2.2.2(7010515aebf7f15e3f6fd94d16578921)': + '@btst/db@2.2.2(84c0473dacd82373aa0e1c019674ae07)': dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) + better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) transitivePeerDependencies: - '@better-auth/utils' - '@better-fetch/fetch' @@ -11581,14 +13608,12 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@csstools/color-helpers@6.0.2': - optional: true + '@csstools/color-helpers@6.0.2': {} '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - optional: true '@csstools/css-color-parser@4.1.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: @@ -11596,20 +13621,16 @@ snapshots: '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - optional: true '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-tokenizer': 4.0.0 - optional: true '@csstools/css-syntax-patches-for-csstree@1.1.5(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 - optional: true - '@csstools/css-tokenizer@4.0.0': - optional: true + '@csstools/css-tokenizer@4.0.0': {} '@date-fns/tz@1.4.1': {} @@ -11680,22 +13701,11 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/core@1.9.1': - dependencies: - '@emnapi/wasi-threads': 1.2.0 - tslib: 2.8.1 - optional: true - '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.2.0': - dependencies: - tslib: 2.8.1 - optional: true - '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 @@ -11940,8 +13950,29 @@ snapshots: eslint: 8.57.1 eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0))': + dependencies: + eslint: 9.39.4(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.2': {} + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.14.0 @@ -11956,12 +13987,38 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.2.0 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@10.0.1(eslint@9.39.4(jiti@2.7.0))': + optionalDependencies: + eslint: 9.39.4(jiti@2.7.0) + '@eslint/js@8.57.1': {} + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + '@exodus/bytes@1.15.1(@noble/hashes@2.0.1)': optionalDependencies: '@noble/hashes': 2.0.1 - optional: true '@fastify/busboy@2.1.1': {} @@ -11990,6 +14047,8 @@ snapshots: '@floating-ui/utils@0.2.11': {} + '@fontsource-variable/geist@5.2.9': {} + '@formatjs/intl-localematcher@0.6.2': dependencies: tslib: 2.8.1 @@ -12003,6 +14062,18 @@ snapshots: '@standard-schema/utils': 0.3.0 react-hook-form: 7.66.1(react@19.2.7) + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 @@ -12015,6 +14086,8 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} + '@humanwhocodes/retry@0.4.3': {} + '@img/colour@1.0.0': optional: true @@ -12112,8 +14185,16 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@inquirer/ansi@1.0.2': {} - + '@inquirer/ansi@1.0.2': {} + + '@inquirer/confirm@5.1.21(@types/node@22.20.0)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.20.0) + '@inquirer/type': 3.0.10(@types/node@22.20.0) + optionalDependencies: + '@types/node': 22.20.0 + optional: true + '@inquirer/confirm@5.1.21(@types/node@24.12.0)': dependencies: '@inquirer/core': 10.3.2(@types/node@24.12.0) @@ -12129,6 +14210,20 @@ snapshots: '@types/node': 25.5.0 optional: true + '@inquirer/core@10.3.2(@types/node@22.20.0)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@22.20.0) + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.20.0 + optional: true + '@inquirer/core@10.3.2(@types/node@24.12.0)': dependencies: '@inquirer/ansi': 1.0.2 @@ -12165,6 +14260,11 @@ snapshots: '@inquirer/figures@1.0.15': {} + '@inquirer/type@3.0.10(@types/node@22.20.0)': + optionalDependencies: + '@types/node': 22.20.0 + optional: true + '@inquirer/type@3.0.10(@types/node@24.12.0)': optionalDependencies: '@types/node': 24.12.0 @@ -12630,7 +14730,7 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.9.1 + '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@tybys/wasm-util': 0.10.1 optional: true @@ -12739,21 +14839,17 @@ snapshots: '@oozcitak/infra': 2.0.2 '@oozcitak/url': 3.0.0 '@oozcitak/util': 10.0.0 - optional: true '@oozcitak/infra@2.0.2': dependencies: '@oozcitak/util': 10.0.0 - optional: true '@oozcitak/url@3.0.0': dependencies: '@oozcitak/infra': 2.0.2 '@oozcitak/util': 10.0.0 - optional: true - '@oozcitak/util@10.0.0': - optional: true + '@oozcitak/util@10.0.0': {} '@open-draft/deferred-promise@2.2.0': {} @@ -12770,9 +14866,75 @@ snapshots: '@orama/orama@3.1.16': {} - '@oxc-project/types@0.134.0': + '@oxc-parser/binding-android-arm-eabi@0.120.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.120.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.120.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.120.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.120.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.120.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.120.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.120.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.120.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.120.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.120.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.120.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.120.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.120.0': optional: true + '@oxc-parser/binding-win32-x64-msvc@0.120.0': + optional: true + + '@oxc-project/types@0.120.0': {} + + '@oxc-project/types@0.134.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.19.1': optional: true @@ -12888,8 +15050,7 @@ snapshots: '@oxc-transform/binding-win32-x64-msvc@0.96.0': optional: true - '@package-json/types@0.0.12': - optional: true + '@package-json/types@0.0.12': {} '@playwright/test@1.56.1': dependencies: @@ -12901,6 +15062,12 @@ snapshots: typescript: 5.9.3 optional: true + '@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(typescript@6.0.3)': + optionalDependencies: + prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) + typescript: 6.0.3 + optional: true + '@prisma/config@7.5.0': dependencies: c12: 3.1.0 @@ -12940,6 +15107,29 @@ snapshots: - typescript optional: true + '@prisma/dev@0.20.0(typescript@6.0.3)': + dependencies: + '@electric-sql/pglite': 0.3.15 + '@electric-sql/pglite-socket': 0.0.20(@electric-sql/pglite@0.3.15) + '@electric-sql/pglite-tools': 0.2.20(@electric-sql/pglite@0.3.15) + '@hono/node-server': 1.19.9(hono@4.11.4) + '@mrleebo/prisma-ast': 0.13.1 + '@prisma/get-platform': 7.2.0 + '@prisma/query-plan-executor': 7.2.0 + foreground-child: 3.3.1 + get-port-please: 3.2.0 + hono: 4.11.4 + http-status-codes: 2.3.0 + pathe: 2.0.3 + proper-lockfile: 4.1.2 + remeda: 2.33.4 + std-env: 3.10.0 + valibot: 1.2.0(typescript@6.0.3) + zeptomatch: 2.1.0 + transitivePeerDependencies: + - typescript + optional: true + '@prisma/engines-version@7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e': optional: true @@ -12980,8 +15170,21 @@ snapshots: '@radix-ui/number@1.1.1': {} + '@radix-ui/number@1.1.2': {} + '@radix-ui/primitive@1.1.3': {} + '@radix-ui/primitive@1.1.4': {} + + '@radix-ui/react-accessible-icon@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -13008,6 +15211,23 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-accordion@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collapsible': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13022,6 +15242,28 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-alert-dialog@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-dialog': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-arrow@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -13031,6 +15273,15 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-aspect-ratio@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -13066,6 +15317,19 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-avatar@1.2.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13082,6 +15346,22 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-checkbox@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13098,6 +15378,34 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-collapsible@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-collection@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.7) @@ -13116,6 +15424,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.14)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13130,6 +15444,19 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-context-menu@2.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-menu': 2.1.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-context@1.1.2(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 @@ -13142,6 +15469,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-context@1.1.4(@types/react@19.2.14)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13164,12 +15497,40 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-dialog@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-direction@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-direction@1.1.2(@types/react@19.2.14)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13183,6 +15544,19 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-dismissable-layer@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13198,12 +15572,44 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-dropdown-menu@2.1.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-menu': 2.1.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.14)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-focus-scope@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.7) @@ -13215,6 +15621,20 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-form@0.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-label': 2.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13246,6 +15666,23 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-hover-card@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-icons@1.3.2(react@19.2.7)': dependencies: react: 19.2.7 @@ -13257,6 +15694,22 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-id@1.1.2(@types/react@19.2.14)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-label@2.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -13301,6 +15754,32 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-menu@2.1.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-popper': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13319,6 +15798,24 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-menubar@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-menu': 2.1.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13341,6 +15838,48 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-navigation-menu@1.2.17(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-one-time-password-field@0.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/number': 1.1.1 @@ -13377,6 +15916,22 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-password-toggle-field@0.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13400,6 +15955,29 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-popover@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-popper': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -13418,6 +15996,34 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-popper@1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-portal@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -13438,6 +16044,15 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.7) @@ -13456,6 +16071,25 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-progress@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.7) @@ -13484,6 +16118,24 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-radio-group@1.4.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13501,6 +16153,23 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-roving-focus@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/number': 1.1.1 @@ -13518,6 +16187,23 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-scroll-area@1.2.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/number': 1.1.1 @@ -13547,6 +16233,45 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-select@2.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-popper': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-separator@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -13584,6 +16309,25 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-slider@1.4.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.7) @@ -13598,6 +16342,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-slot@1.3.0(@types/react@19.2.14)(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13613,6 +16364,21 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-switch@1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13629,6 +16395,22 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-tabs@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13649,6 +16431,26 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-toast@1.2.18(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13664,6 +16466,21 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-toggle-group@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13675,6 +16492,17 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-toggle@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13690,6 +16518,41 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-toolbar@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-separator': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle-group': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-tooltip@1.2.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-popper': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -13716,6 +16579,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.14)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.7) @@ -13724,6 +16593,14 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.14)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.7) @@ -13731,6 +16608,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.14)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.7) @@ -13738,6 +16622,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-use-escape-keydown@1.1.3(@types/react@19.2.14)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 @@ -13745,18 +16636,36 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.14)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.14)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-use-previous@1.1.2(@types/react@19.2.14)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/rect': 1.1.1 @@ -13764,6 +16673,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.14)(react@19.2.7)': + dependencies: + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.14)(react@19.2.7)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.7) @@ -13771,6 +16687,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.14)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.14 + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -13780,8 +16703,19 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/react-visually-hidden@1.2.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/rect@1.1.1': {} + '@radix-ui/rect@1.1.2': {} + '@remirror/core-constants@3.0.0': {} '@rolldown/binding-android-arm64@1.1.0': @@ -13833,11 +16767,11 @@ snapshots: '@rolldown/binding-win32-x64-msvc@1.1.0': optional: true - '@rolldown/pluginutils@1.0.0-beta.40': - optional: true + '@rolldown/pluginutils@1.0.0-beta.40': {} - '@rolldown/pluginutils@1.0.1': - optional: true + '@rolldown/pluginutils@1.0.0-rc.3': {} + + '@rolldown/pluginutils@1.0.1': {} '@rollup/plugin-alias@5.1.1(rollup@4.53.2)': optionalDependencies: @@ -14346,6 +17280,40 @@ snapshots: dependencies: tslib: 2.8.1 + '@solid-primitives/event-listener@2.4.5(solid-js@1.9.12)': + dependencies: + '@solid-primitives/utils': 6.4.0(solid-js@1.9.12) + solid-js: 1.9.12 + + '@solid-primitives/keyboard@1.3.5(solid-js@1.9.12)': + dependencies: + '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.12) + '@solid-primitives/rootless': 1.5.3(solid-js@1.9.12) + '@solid-primitives/utils': 6.4.0(solid-js@1.9.12) + solid-js: 1.9.12 + + '@solid-primitives/resize-observer@2.1.5(solid-js@1.9.12)': + dependencies: + '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.12) + '@solid-primitives/rootless': 1.5.3(solid-js@1.9.12) + '@solid-primitives/static-store': 0.1.3(solid-js@1.9.12) + '@solid-primitives/utils': 6.4.0(solid-js@1.9.12) + solid-js: 1.9.12 + + '@solid-primitives/rootless@1.5.3(solid-js@1.9.12)': + dependencies: + '@solid-primitives/utils': 6.4.0(solid-js@1.9.12) + solid-js: 1.9.12 + + '@solid-primitives/static-store@0.1.3(solid-js@1.9.12)': + dependencies: + '@solid-primitives/utils': 6.4.0(solid-js@1.9.12) + solid-js: 1.9.12 + + '@solid-primitives/utils@6.4.0(solid-js@1.9.12)': + dependencies: + solid-js: 1.9.12 + '@stackblitz/sdk@1.11.0': {} '@standard-schema/spec@1.0.0': {} @@ -14354,6 +17322,16 @@ snapshots: '@standard-schema/utils@0.3.0': {} + '@stylistic/eslint-plugin@5.10.0(eslint@9.39.4(jiti@2.7.0))': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@typescript-eslint/types': 8.61.0 + eslint: 9.39.4(jiti@2.7.0) + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + estraverse: 5.3.0 + picomatch: 4.0.4 + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -14368,42 +17346,88 @@ snapshots: source-map-js: 1.2.1 tailwindcss: 4.2.2 + '@tailwindcss/node@4.3.2': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.6 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.2 + '@tailwindcss/oxide-android-arm64@4.2.2': optional: true + '@tailwindcss/oxide-android-arm64@4.3.2': + optional: true + '@tailwindcss/oxide-darwin-arm64@4.2.2': optional: true + '@tailwindcss/oxide-darwin-arm64@4.3.2': + optional: true + '@tailwindcss/oxide-darwin-x64@4.2.2': optional: true + '@tailwindcss/oxide-darwin-x64@4.3.2': + optional: true + '@tailwindcss/oxide-freebsd-x64@4.2.2': optional: true + '@tailwindcss/oxide-freebsd-x64@4.3.2': + optional: true + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': optional: true + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': + optional: true + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': optional: true + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': + optional: true + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': optional: true + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': + optional: true + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': optional: true + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': + optional: true + '@tailwindcss/oxide-linux-x64-musl@4.2.2': optional: true + '@tailwindcss/oxide-linux-x64-musl@4.3.2': + optional: true + '@tailwindcss/oxide-wasm32-wasi@4.2.2': optional: true + '@tailwindcss/oxide-wasm32-wasi@4.3.2': + optional: true + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': optional: true + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': + optional: true + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': optional: true + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': + optional: true + '@tailwindcss/oxide@4.2.2': optionalDependencies: '@tailwindcss/oxide-android-arm64': 4.2.2 @@ -14419,6 +17443,21 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 + '@tailwindcss/oxide@4.3.2': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.2 + '@tailwindcss/oxide-darwin-arm64': 4.3.2 + '@tailwindcss/oxide-darwin-x64': 4.3.2 + '@tailwindcss/oxide-freebsd-x64': 4.3.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.2 + '@tailwindcss/oxide-linux-x64-musl': 4.3.2 + '@tailwindcss/oxide-wasm32-wasi': 4.3.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.2 + '@tailwindcss/postcss@4.2.2': dependencies: '@alloc/quick-lru': 5.2.0 @@ -14432,16 +17471,135 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 4.2.2 - '@tanstack/history@1.161.6': - optional: true + '@tailwindcss/vite@4.3.2(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': + dependencies: + '@tailwindcss/node': 4.3.2 + '@tailwindcss/oxide': 4.3.2 + tailwindcss: 4.3.2 + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + + '@tanstack/devtools-client@0.0.8': + dependencies: + '@tanstack/devtools-event-client': 0.5.0 + + '@tanstack/devtools-event-bus@0.4.2': + dependencies: + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@tanstack/devtools-event-client@0.5.0': {} + + '@tanstack/devtools-ui@0.6.0(csstype@3.2.3)(solid-js@1.9.12)': + dependencies: + clsx: 2.1.1 + dayjs: 1.11.21 + goober: 2.1.19(csstype@3.2.3) + solid-js: 1.9.12 + transitivePeerDependencies: + - csstype + + '@tanstack/devtools-vite@0.8.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': + dependencies: + '@tanstack/devtools-client': 0.0.8 + '@tanstack/devtools-event-bus': 0.4.2 + chalk: 5.6.2 + launch-editor: 2.14.1 + magic-string: 0.30.21 + oxc-parser: 0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + picomatch: 4.0.4 + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + - bufferutil + - utf-8-validate + + '@tanstack/devtools@0.12.5(csstype@3.2.3)(solid-js@1.9.12)': + dependencies: + '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.12) + '@solid-primitives/keyboard': 1.3.5(solid-js@1.9.12) + '@solid-primitives/resize-observer': 2.1.5(solid-js@1.9.12) + '@tanstack/devtools-client': 0.0.8 + '@tanstack/devtools-event-bus': 0.4.2 + '@tanstack/devtools-ui': 0.6.0(csstype@3.2.3)(solid-js@1.9.12) + clsx: 2.1.1 + goober: 2.1.19(csstype@3.2.3) + solid-js: 1.9.12 + transitivePeerDependencies: + - bufferutil + - csstype + - utf-8-validate + + '@tanstack/eslint-config@0.4.0(@typescript-eslint/utils@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@eslint/js': 10.0.1(eslint@9.39.4(jiti@2.7.0)) + '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.7.0)) + eslint: 9.39.4(jiti@2.7.0) + eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-n: 17.24.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + globals: 17.7.0 + typescript-eslint: 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + vue-eslint-parser: 10.4.1(eslint@9.39.4(jiti@2.7.0)) + transitivePeerDependencies: + - '@typescript-eslint/utils' + - eslint-import-resolver-node + - supports-color + - typescript + + '@tanstack/history@1.161.6': {} '@tanstack/query-core@5.90.10': {} + '@tanstack/query-devtools@5.101.2': {} + + '@tanstack/react-devtools@0.10.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)': + dependencies: + '@tanstack/devtools': 0.12.5(csstype@3.2.3)(solid-js@1.9.12) + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + transitivePeerDependencies: + - bufferutil + - csstype + - solid-js + - utf-8-validate + + '@tanstack/react-query-devtools@5.101.2(@tanstack/react-query@5.90.10(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/query-devtools': 5.101.2 + '@tanstack/react-query': 5.90.10(react@19.2.7) + react: 19.2.7 + '@tanstack/react-query@5.90.10(react@19.2.7)': dependencies: '@tanstack/query-core': 5.90.10 react: 19.2.7 + '@tanstack/react-router-devtools@1.167.0(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.168.9)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-devtools-core': 1.168.0(@tanstack/router-core@1.168.9)(csstype@3.2.3) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@tanstack/router-core': 1.168.9 + transitivePeerDependencies: + - csstype + + '@tanstack/react-router-ssr-query@1.167.1(@tanstack/query-core@5.90.10)(@tanstack/react-query@5.90.10(react@19.2.7))(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.168.9)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/query-core': 5.90.10 + '@tanstack/react-query': 5.90.10(react@19.2.7) + '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-ssr-query-core': 1.169.1(@tanstack/query-core@5.90.10)(@tanstack/router-core@1.168.9) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + transitivePeerDependencies: + - '@tanstack/router-core' + '@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@tanstack/history': 1.161.6 @@ -14450,7 +17608,6 @@ snapshots: isbot: 5.1.42 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - optional: true '@tanstack/react-start-client@1.166.25(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: @@ -14459,7 +17616,6 @@ snapshots: '@tanstack/start-client-core': 1.167.9 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - optional: true '@tanstack/react-start-server@1.166.25(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: @@ -14472,21 +17628,53 @@ snapshots: react-dom: 19.2.7(react@19.2.7) transitivePeerDependencies: - crossws + + '@tanstack/react-start-server@1.166.25(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/history': 1.161.6 + '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-core': 1.168.9 + '@tanstack/start-client-core': 1.167.9 + '@tanstack/start-server-core': 1.167.9(crossws@0.4.9(srvx@0.11.20)) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + transitivePeerDependencies: + - crossws optional: true - '@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tanstack/react-start-client': 1.166.25(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tanstack/react-start-server': 1.166.25(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tanstack/router-utils': 1.162.2 '@tanstack/start-client-core': 1.167.9 - '@tanstack/start-plugin-core': 1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.6(srvx@0.11.16))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/start-plugin-core': 1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.6(srvx@0.11.16))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) '@tanstack/start-server-core': 1.167.9(crossws@0.4.6(srvx@0.11.16)) pathe: 2.0.3 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + transitivePeerDependencies: + - '@rsbuild/core' + - crossws + - supports-color + - vite-plugin-solid + - webpack + + '@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-start-client': 1.166.25(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-start-server': 1.166.25(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-utils': 1.162.2 + '@tanstack/start-client-core': 1.167.9 + '@tanstack/start-plugin-core': 1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.9(srvx@0.11.20))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/start-server-core': 1.167.9(crossws@0.4.9(srvx@0.11.20)) + pathe: 2.0.3 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@rsbuild/core' - crossws @@ -14501,7 +17689,6 @@ snapshots: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) use-sync-external-store: 1.6.0(react@19.2.7) - optional: true '@tanstack/router-core@1.168.9': dependencies: @@ -14509,7 +17696,14 @@ snapshots: cookie-es: 2.0.1 seroval: 1.5.4 seroval-plugins: 1.5.4(seroval@1.5.4) - optional: true + + '@tanstack/router-devtools-core@1.168.0(@tanstack/router-core@1.168.9)(csstype@3.2.3)': + dependencies: + '@tanstack/router-core': 1.168.9 + clsx: 2.1.1 + goober: 2.1.19(csstype@3.2.3) + optionalDependencies: + csstype: 3.2.3 '@tanstack/router-generator@1.166.24': dependencies: @@ -14523,9 +17717,29 @@ snapshots: zod: 3.25.76 transitivePeerDependencies: - supports-color - optional: true - '@tanstack/router-plugin@1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/router-plugin@1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.168.9 + '@tanstack/router-generator': 1.166.24 + '@tanstack/router-utils': 1.161.6 + '@tanstack/virtual-file-routes': 1.161.7 + chokidar: 3.6.0 + unplugin: 2.3.11 + zod: 3.25.76 + optionalDependencies: + '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) @@ -14542,11 +17756,16 @@ snapshots: zod: 3.25.76 optionalDependencies: '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color optional: true + '@tanstack/router-ssr-query-core@1.169.1(@tanstack/query-core@5.90.10)(@tanstack/router-core@1.168.9)': + dependencies: + '@tanstack/query-core': 5.90.10 + '@tanstack/router-core': 1.168.9 + '@tanstack/router-utils@1.161.6': dependencies: '@babel/core': 7.29.7 @@ -14560,7 +17779,6 @@ snapshots: tinyglobby: 0.2.17 transitivePeerDependencies: - supports-color - optional: true '@tanstack/router-utils@1.162.2': dependencies: @@ -14574,7 +17792,6 @@ snapshots: tinyglobby: 0.2.17 transitivePeerDependencies: - supports-color - optional: true '@tanstack/start-client-core@1.167.9': dependencies: @@ -14582,12 +17799,10 @@ snapshots: '@tanstack/start-fn-stubs': 1.161.6 '@tanstack/start-storage-context': 1.166.23 seroval: 1.5.4 - optional: true - '@tanstack/start-fn-stubs@1.161.6': - optional: true + '@tanstack/start-fn-stubs@1.161.6': {} - '@tanstack/start-plugin-core@1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.6(srvx@0.11.16))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/start-plugin-core@1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.6(srvx@0.11.16))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.29.7 @@ -14595,7 +17810,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.40 '@tanstack/router-core': 1.168.9 '@tanstack/router-generator': 1.166.24 - '@tanstack/router-plugin': 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/router-plugin': 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) '@tanstack/router-utils': 1.161.6 '@tanstack/start-client-core': 1.167.9 '@tanstack/start-server-core': 1.167.9(crossws@0.4.6(srvx@0.11.16)) @@ -14607,8 +17822,40 @@ snapshots: srvx: 0.11.16 tinyglobby: 0.2.17 ufo: 1.6.4 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) - vitefu: 1.1.3(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + vitefu: 1.1.3(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + xmlbuilder2: 4.0.3 + zod: 3.25.76 + transitivePeerDependencies: + - '@rsbuild/core' + - '@tanstack/react-router' + - crossws + - supports-color + - vite-plugin-solid + - webpack + + '@tanstack/start-plugin-core@1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.9(srvx@0.11.20))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/core': 7.29.7 + '@babel/types': 7.29.7 + '@rolldown/pluginutils': 1.0.0-beta.40 + '@tanstack/router-core': 1.168.9 + '@tanstack/router-generator': 1.166.24 + '@tanstack/router-plugin': 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/router-utils': 1.161.6 + '@tanstack/start-client-core': 1.167.9 + '@tanstack/start-server-core': 1.167.9(crossws@0.4.9(srvx@0.11.20)) + cheerio: 1.2.0 + exsolve: 1.0.8 + pathe: 2.0.3 + picomatch: 4.0.4 + source-map: 0.7.6 + srvx: 0.11.16 + tinyglobby: 0.2.17 + ufo: 1.6.4 + vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vitefu: 1.1.3(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) xmlbuilder2: 4.0.3 zod: 3.25.76 transitivePeerDependencies: @@ -14630,18 +17877,47 @@ snapshots: seroval: 1.5.4 transitivePeerDependencies: - crossws + + '@tanstack/start-server-core@1.167.9(crossws@0.4.9(srvx@0.11.20))': + dependencies: + '@tanstack/history': 1.161.6 + '@tanstack/router-core': 1.168.9 + '@tanstack/start-client-core': 1.167.9 + '@tanstack/start-storage-context': 1.166.23 + h3-v2: h3@2.0.1-rc.16(crossws@0.4.9(srvx@0.11.20)) + seroval: 1.5.4 + transitivePeerDependencies: + - crossws optional: true '@tanstack/start-storage-context@1.166.23': dependencies: '@tanstack/router-core': 1.168.9 - optional: true - '@tanstack/store@0.9.3': - optional: true + '@tanstack/store@0.9.3': {} - '@tanstack/virtual-file-routes@1.161.7': - optional: true + '@tanstack/virtual-file-routes@1.161.7': {} + + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.2 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@babel/runtime': 7.29.2 + '@testing-library/dom': 10.4.1 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) '@tiptap/core@3.20.0(@tiptap/pm@3.15.3)': dependencies: @@ -14922,6 +18198,29 @@ snapshots: tslib: 2.8.1 optional: true + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.7 + '@types/bun@1.3.2(@types/react@19.2.14)': dependencies: bun-types: 1.3.2(@types/react@19.2.14) @@ -14943,6 +18242,8 @@ snapshots: dependencies: diff: 8.0.4 + '@types/esrecurse@4.3.1': {} + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.8 @@ -14963,6 +18264,8 @@ snapshots: '@types/through': 0.0.33 rxjs: 6.6.7 + '@types/json-schema@7.0.15': {} + '@types/json5@0.0.29': {} '@types/katex@0.16.7': {} @@ -14998,6 +18301,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@22.20.0': + dependencies: + undici-types: 6.21.0 + '@types/node@24.0.3': dependencies: undici-types: 7.8.0 @@ -15073,6 +18380,22 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/type-utils': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.62.1 + eslint: 9.39.4(jiti@2.7.0) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.58.0 @@ -15085,6 +18408,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.62.1 + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/project-service@8.58.0(typescript@5.9.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.58.0(typescript@5.9.3) @@ -15094,36 +18429,48 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.61.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.62.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@5.9.3) - '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@5.9.3) + '@typescript-eslint/types': 8.62.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color optional: true + '@typescript-eslint/project-service@8.62.1(typescript@6.0.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@6.0.3) + '@typescript-eslint/types': 8.62.1 + debug: 4.4.3 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.58.0': dependencies: '@typescript-eslint/types': 8.58.0 '@typescript-eslint/visitor-keys': 8.58.0 - '@typescript-eslint/scope-manager@8.61.0': + '@typescript-eslint/scope-manager@8.62.1': dependencies: - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/visitor-keys': 8.61.0 - optional: true + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/visitor-keys': 8.62.1 '@typescript-eslint/tsconfig-utils@8.58.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.61.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.62.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 optional: true + '@typescript-eslint/tsconfig-utils@8.62.1(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + '@typescript-eslint/type-utils@8.58.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.58.0 @@ -15136,10 +18483,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/type-utils@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/types@8.58.0': {} - '@typescript-eslint/types@8.61.0': - optional: true + '@typescript-eslint/types@8.61.0': {} + + '@typescript-eslint/types@8.62.1': {} '@typescript-eslint/typescript-estree@8.58.0(typescript@5.9.3)': dependencies: @@ -15156,12 +18516,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.61.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.62.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.61.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@5.9.3) - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/visitor-keys': 8.61.0 + '@typescript-eslint/project-service': 8.62.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@5.9.3) + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/visitor-keys': 8.62.1 debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.4 @@ -15172,6 +18532,21 @@ snapshots: - supports-color optional: true + '@typescript-eslint/typescript-estree@8.62.1(typescript@6.0.3)': + dependencies: + '@typescript-eslint/project-service': 8.62.1(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@6.0.3) + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/visitor-keys': 8.62.1 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.4 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.58.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) @@ -15183,28 +18558,38 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.61.0(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) eslint: 8.57.1 typescript: 5.9.3 transitivePeerDependencies: - supports-color optional: true + '@typescript-eslint/utils@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) + eslint: 9.39.4(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.58.0': dependencies: '@typescript-eslint/types': 8.58.0 eslint-visitor-keys: 5.0.1 - '@typescript-eslint/visitor-keys@8.61.0': + '@typescript-eslint/visitor-keys@8.62.1': dependencies: - '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/types': 8.62.1 eslint-visitor-keys: 5.0.1 - optional: true '@ungap/structured-clone@1.3.0': {} @@ -15346,7 +18731,7 @@ snapshots: '@vercel/analytics@1.6.1(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(vue@3.5.24(typescript@5.9.3))': optionalDependencies: - next: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 vue: 3.5.24(typescript@5.9.3) @@ -15360,6 +18745,18 @@ snapshots: '@vercel/oidc@3.0.3': {} + '@vitejs/plugin-react@5.2.0(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.0-rc.3 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.3 @@ -15368,59 +18765,101 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/expect@4.1.9': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@3.2.4(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.10(@types/node@24.12.0)(typescript@5.9.3) - vite: 7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@3.2.4(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': + '@vitest/mocker@3.2.4(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.10(@types/node@24.12.0)(typescript@5.9.3) - vite: 7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + vite: 7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - '@vitest/mocker@3.2.4(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@3.2.4(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.10(@types/node@25.5.0)(typescript@5.9.3) - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + + '@vitest/mocker@4.1.9(msw@2.12.10(@types/node@22.20.0)(typescript@6.0.3))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': + dependencies: + '@vitest/spy': 4.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.12.10(@types/node@22.20.0)(typescript@6.0.3) + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 + '@vitest/pretty-format@4.1.9': + dependencies: + tinyrainbow: 3.1.0 + '@vitest/runner@3.2.4': dependencies: '@vitest/utils': 3.2.4 pathe: 2.0.3 strip-literal: 3.1.0 + '@vitest/runner@4.1.9': + dependencies: + '@vitest/utils': 4.1.9 + pathe: 2.0.3 + '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 magic-string: 0.30.21 pathe: 2.0.3 + '@vitest/snapshot@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + '@vitest/utils': 4.1.9 + magic-string: 0.30.21 + pathe: 2.0.3 + '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.4 + '@vitest/spy@4.1.9': {} + '@vitest/utils@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 loupe: 3.2.1 tinyrainbow: 2.0.0 + '@vitest/utils@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + '@vue/compiler-core@3.5.24': dependencies: '@babel/parser': 7.29.7 @@ -15473,6 +18912,13 @@ snapshots: '@vue/shared': 3.5.24 vue: 3.5.24(typescript@5.9.3) + '@vue/server-renderer@3.5.24(vue@3.5.24(typescript@6.0.3))': + dependencies: + '@vue/compiler-ssr': 3.5.24 + '@vue/shared': 3.5.24 + vue: 3.5.24(typescript@6.0.3) + optional: true + '@vue/shared@3.5.24': {} accepts@2.0.0: @@ -15484,6 +18930,10 @@ snapshots: dependencies: acorn: 8.15.0 + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn-walk@8.3.4: dependencies: acorn: 8.15.0 @@ -15541,10 +18991,11 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + ansis@4.2.0: {} - ansis@4.3.1: - optional: true + ansis@4.3.1: {} anymatch@3.1.3: dependencies: @@ -15561,6 +19012,10 @@ snapshots: dependencies: tslib: 2.8.1 + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + aria-query@5.3.2: {} array-buffer-byte-length@1.0.2: @@ -15681,7 +19136,6 @@ snapshots: '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - optional: true babel-plugin-react-compiler@1.0.0: dependencies: @@ -15700,7 +19154,7 @@ snapshots: basic-ftp@5.0.5: {} - better-auth@1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)): + better-auth@1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)): dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) '@better-auth/drizzle-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1) @@ -15721,20 +19175,55 @@ snapshots: zod: 4.4.3 optionalDependencies: '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3) - '@tanstack/react-start': 1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/react-start': 1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) mongodb: 6.21.0(socks@2.8.7) mysql2: 3.15.3 - next: 16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) solid-js: 1.9.12 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) vue: 3.5.24(typescript@5.9.3) transitivePeerDependencies: - '@cloudflare/workers-types' - '@opentelemetry/api' + better-auth@1.6.16(fb120b41aed529c3fb40587bdf11e900): + dependencies: + '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) + '@better-auth/drizzle-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1) + '@better-auth/kysely-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(kysely@0.29.2) + '@better-auth/memory-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1) + '@better-auth/mongo-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(mongodb@6.21.0(socks@2.8.7)) + '@better-auth/prisma-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(typescript@6.0.3))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)) + '@better-auth/telemetry': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2) + '@better-auth/utils': 0.4.1 + '@better-fetch/fetch': 1.2.2 + '@noble/ciphers': 2.1.1 + '@noble/hashes': 2.0.1 + better-call: 1.3.6(zod@4.4.3) + defu: 6.1.7 + jose: 6.2.0 + kysely: 0.29.2 + nanostores: 1.1.1 + zod: 4.4.3 + optionalDependencies: + '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(typescript@6.0.3) + '@tanstack/react-start': 1.167.16(crossws@0.4.6(srvx@0.11.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + mongodb: 6.21.0(socks@2.8.7) + mysql2: 3.15.3 + next: 16.1.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + solid-js: 1.9.12 + vitest: 4.1.9(@opentelemetry/api@1.9.0)(@types/node@22.20.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@22.20.0)(typescript@6.0.3))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + vue: 3.5.24(typescript@6.0.3) + transitivePeerDependencies: + - '@cloudflare/workers-types' + - '@opentelemetry/api' + better-call@1.3.6(zod@4.4.3): dependencies: '@better-auth/utils': 0.4.0 @@ -15747,7 +19236,6 @@ snapshots: bidi-js@1.0.3: dependencies: require-from-string: 2.0.2 - optional: true binary-extensions@2.3.0: {} @@ -15849,9 +19337,9 @@ snapshots: confbox: 0.2.4 defu: 6.1.7 dotenv: 16.6.1 - exsolve: 1.0.8 + exsolve: 1.1.0 giget: 2.0.0 - jiti: 2.6.1 + jiti: 2.7.0 ohash: 2.0.11 pathe: 2.0.3 perfect-debounce: 1.0.0 @@ -15921,6 +19409,8 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chai@6.2.2: {} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -15982,7 +19472,6 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.2.2 - optional: true cheerio@1.2.0: dependencies: @@ -15997,7 +19486,6 @@ snapshots: parse5-parser-stream: 7.1.2 undici: 7.27.2 whatwg-mimetype: 4.0.0 - optional: true chevrotain@10.5.0: dependencies: @@ -16111,8 +19599,7 @@ snapshots: commander@8.3.0: {} - comment-parser@1.4.7: - optional: true + comment-parser@1.4.7: {} commondir@1.0.1: {} @@ -16137,8 +19624,7 @@ snapshots: convert-source-map@2.0.0: {} - cookie-es@2.0.1: - optional: true + cookie-es@2.0.1: {} cookie-signature@1.2.2: {} @@ -16162,6 +19648,15 @@ snapshots: optionalDependencies: typescript: 5.9.3 + cosmiconfig@9.0.1(typescript@6.0.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.2.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 6.0.3 + create-require@1.1.1: {} crelt@1.0.6: {} @@ -16175,7 +19670,10 @@ snapshots: crossws@0.4.6(srvx@0.11.16): optionalDependencies: srvx: 0.11.16 - optional: true + + crossws@0.4.9(srvx@0.11.20): + optionalDependencies: + srvx: 0.11.20 css-declaration-sorter@7.3.0(postcss@8.5.6): dependencies: @@ -16257,7 +19755,6 @@ snapshots: '@csstools/css-syntax-patches-for-csstree': 1.1.5(css-tree@3.2.1) css-tree: 3.2.1 lru-cache: 11.5.1 - optional: true csstype@3.2.3: {} @@ -16273,7 +19770,6 @@ snapshots: whatwg-url: 16.0.1(@noble/hashes@2.0.1) transitivePeerDependencies: - '@noble/hashes' - optional: true data-view-buffer@1.0.2: dependencies: @@ -16297,6 +19793,13 @@ snapshots: date-fns@4.1.0: {} + dayjs@1.11.21: {} + + db0@0.3.4(@electric-sql/pglite@0.3.15)(mysql2@3.15.3): + optionalDependencies: + '@electric-sql/pglite': 0.3.15 + mysql2: 3.15.3 + debug@3.2.7: dependencies: ms: 2.1.3 @@ -16305,8 +19808,7 @@ snapshots: dependencies: ms: 2.1.3 - decimal.js@10.6.0: - optional: true + decimal.js@10.6.0: {} decode-named-character-reference@1.2.0: dependencies: @@ -16408,6 +19910,8 @@ snapshots: dependencies: esutils: 2.0.3 + dom-accessibility-api@0.5.16: {} + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -16490,25 +19994,34 @@ snapshots: dependencies: iconv-lite: 0.6.3 whatwg-encoding: 3.1.1 - optional: true enhanced-resolve@5.20.1: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 + enhanced-resolve@5.21.6: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + entities@4.5.0: {} entities@6.0.1: {} - entities@7.0.1: - optional: true + entities@7.0.1: {} - entities@8.0.0: - optional: true + entities@8.0.0: {} env-paths@2.2.1: {} + env-runner@0.1.16: + dependencies: + crossws: 0.4.9(srvx@0.11.20) + exsolve: 1.1.0 + httpxy: 0.5.4 + srvx: 0.11.20 + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -16595,6 +20108,8 @@ snapshots: es-module-lexer@1.7.0: {} + es-module-lexer@2.3.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -16716,7 +20231,6 @@ snapshots: '@esbuild/win32-arm64': 0.28.0 '@esbuild/win32-ia32': 0.28.0 '@esbuild/win32-x64': 0.28.0 - optional: true escalade@3.2.0: {} @@ -16736,7 +20250,12 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-next@15.3.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3): + eslint-compat-utils@0.5.1(eslint@9.39.4(jiti@2.7.0)): + dependencies: + eslint: 9.39.4(jiti@2.7.0) + semver: 7.8.4 + + eslint-config-next@15.3.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 15.3.4 '@rushstack/eslint-patch': 1.15.0 @@ -16744,7 +20263,7 @@ snapshots: '@typescript-eslint/parser': 8.58.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0)(eslint@8.57.1) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) @@ -16762,7 +20281,6 @@ snapshots: stable-hash-x: 0.2.0 optionalDependencies: unrs-resolver: 1.12.2 - optional: true eslint-import-resolver-node@0.3.9: dependencies: @@ -16772,7 +20290,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0)(eslint@8.57.1): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -16784,7 +20302,7 @@ snapshots: unrs-resolver: 1.11.1 optionalDependencies: eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) - eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) + eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) transitivePeerDependencies: - supports-color @@ -16795,11 +20313,18 @@ snapshots: '@typescript-eslint/parser': 8.58.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): + eslint-plugin-es-x@7.8.0(eslint@9.39.4(jiti@2.7.0)): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + eslint: 9.39.4(jiti@2.7.0) + eslint-compat-utils: 0.5.1(eslint@9.39.4(jiti@2.7.0)) + + eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): dependencies: '@package-json/types': 0.0.12 '@typescript-eslint/types': 8.61.0 @@ -16813,12 +20338,31 @@ snapshots: stable-hash-x: 0.2.0 unrs-resolver: 1.12.2 optionalDependencies: - '@typescript-eslint/utils': 8.61.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.1(eslint@8.57.1)(typescript@5.9.3) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color optional: true + eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.7.0)): + dependencies: + '@package-json/types': 0.0.12 + '@typescript-eslint/types': 8.61.0 + comment-parser: 1.4.7 + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + eslint-import-context: 0.1.9(unrs-resolver@1.12.2) + is-glob: 4.0.3 + minimatch: 10.2.5 + semver: 7.8.4 + stable-hash-x: 0.2.0 + unrs-resolver: 1.12.2 + optionalDependencies: + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 @@ -16867,6 +20411,21 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 + eslint-plugin-n@17.24.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + enhanced-resolve: 5.20.1 + eslint: 9.39.4(jiti@2.7.0) + eslint-plugin-es-x: 7.8.0(eslint@9.39.4(jiti@2.7.0)) + get-tsconfig: 4.14.0 + globals: 15.15.0 + globrex: 0.1.2 + ignore: 5.3.2 + semver: 7.8.4 + ts-declaration-location: 1.0.7(typescript@6.0.3) + transitivePeerDependencies: + - typescript + eslint-plugin-react-hooks@5.2.0(eslint@8.57.1): dependencies: eslint: 8.57.1 @@ -16898,8 +20457,22 @@ snapshots: esrecurse: 4.3.0 estraverse: 5.3.0 + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.8 + esrecurse: 4.3.0 + estraverse: 5.3.0 + eslint-visitor-keys@3.4.3: {} + eslint-visitor-keys@4.2.1: {} + eslint-visitor-keys@5.0.1: {} eslint@8.57.1: @@ -16945,6 +20518,59 @@ snapshots: transitivePeerDependencies: - supports-color + eslint@9.39.4(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + espree@11.2.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 5.0.1 + espree@9.6.1: dependencies: acorn: 8.15.0 @@ -17041,6 +20667,8 @@ snapshots: expect-type@1.2.2: {} + expect-type@1.4.0: {} + express-rate-limit@8.3.2(express@5.2.1): dependencies: express: 5.2.1 @@ -17081,6 +20709,8 @@ snapshots: exsolve@1.0.8: {} + exsolve@1.1.0: {} + extend@3.0.2: {} external-editor@3.1.0: @@ -17166,6 +20796,10 @@ snapshots: dependencies: flat-cache: 3.2.0 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -17198,6 +20832,11 @@ snapshots: keyv: 4.5.4 rimraf: 3.0.2 + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + flatted@3.3.3: {} for-each@0.3.5: @@ -17279,7 +20918,7 @@ snapshots: '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@types/react': 19.2.14 lucide-react: 0.522.0(react@19.2.7) - next: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) react-router: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -17299,7 +20938,7 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - fumadocs-mdx@13.0.6(fumadocs-core@16.0.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@types/react@19.2.14)(lucide-react@0.522.0(react@19.2.7))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): + fumadocs-mdx@13.0.6(fumadocs-core@16.0.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@types/react@19.2.14)(lucide-react@0.522.0(react@19.2.7))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.0.3)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): dependencies: '@mdx-js/mdx': 3.1.1 '@standard-schema/spec': 1.1.0 @@ -17320,9 +20959,9 @@ snapshots: unist-util-visit: 5.0.0 zod: 4.4.3 optionalDependencies: - next: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 - vite: 7.3.1(@types/node@24.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + vite: 7.3.1(@types/node@24.0.3)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -17368,7 +21007,7 @@ snapshots: tailwind-merge: 3.6.0 optionalDependencies: '@types/react': 19.2.14 - next: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) tailwindcss: 4.2.2 transitivePeerDependencies: - '@mixedbread/sdk' @@ -17454,7 +21093,6 @@ snapshots: get-tsconfig@4.14.0: dependencies: resolve-pkg-maps: 1.0.0 - optional: true get-uri@6.0.5: dependencies: @@ -17496,6 +21134,12 @@ snapshots: dependencies: type-fest: 0.20.2 + globals@14.0.0: {} + + globals@15.15.0: {} + + globals@17.7.0: {} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 @@ -17512,6 +21156,12 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + globrex@0.1.2: {} + + goober@2.1.19(csstype@3.2.3): + dependencies: + csstype: 3.2.3 + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -17534,11 +21184,25 @@ snapshots: h3@2.0.1-rc.16(crossws@0.4.6(srvx@0.11.16)): dependencies: rou3: 0.8.1 - srvx: 0.11.16 + srvx: 0.11.20 optionalDependencies: crossws: 0.4.6(srvx@0.11.16) + + h3@2.0.1-rc.16(crossws@0.4.9(srvx@0.11.20)): + dependencies: + rou3: 0.8.1 + srvx: 0.11.20 + optionalDependencies: + crossws: 0.4.9(srvx@0.11.20) optional: true + h3@2.0.1-rc.22(crossws@0.4.6(srvx@0.11.16)): + dependencies: + rou3: 0.8.1 + srvx: 0.11.16 + optionalDependencies: + crossws: 0.4.6(srvx@0.11.16) + handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -17745,12 +21409,13 @@ snapshots: hookable@5.5.3: {} + hookable@6.1.1: {} + html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1): dependencies: '@exodus/bytes': 1.15.1(@noble/hashes@2.0.1) transitivePeerDependencies: - '@noble/hashes' - optional: true html-url-attributes@3.0.1: {} @@ -17762,7 +21427,6 @@ snapshots: domhandler: 5.0.3 domutils: 3.2.2 entities: 7.0.1 - optional: true http-errors@2.0.1: dependencies: @@ -17789,6 +21453,8 @@ snapshots: transitivePeerDependencies: - supports-color + httpxy@0.5.4: {} + human-signals@2.1.0: {} human-signals@8.0.1: {} @@ -17800,7 +21466,6 @@ snapshots: iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - optional: true iconv-lite@0.7.2: dependencies: @@ -18006,8 +21671,7 @@ snapshots: is-plain-obj@4.1.0: {} - is-potential-custom-element-name@1.0.1: - optional: true + is-potential-custom-element-name@1.0.1: {} is-promise@4.0.0: {} @@ -18085,8 +21749,7 @@ snapshots: isbinaryfile@4.0.10: {} - isbot@5.1.42: - optional: true + isbot@5.1.42: {} isexe@2.0.0: {} @@ -18105,6 +21768,8 @@ snapshots: jiti@2.6.1: {} + jiti@2.7.0: {} + jose@6.2.0: {} js-tokens@4.0.0: {} @@ -18145,7 +21810,6 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' - supports-color - optional: true jsesc@3.1.0: {} @@ -18227,6 +21891,11 @@ snapshots: dependencies: language-subtag-registry: 0.3.23 + launch-editor@2.14.1: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.9.0 + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -18354,8 +22023,7 @@ snapshots: lru-cache@11.2.7: {} - lru-cache@11.5.1: - optional: true + lru-cache@11.5.1: {} lru-cache@5.1.1: dependencies: @@ -18374,10 +22042,16 @@ snapshots: dependencies: react: 19.2.7 + lucide-react@0.545.0(react@19.2.7): + dependencies: + react: 19.2.7 + lucide-react@1.7.0(react@19.2.7): dependencies: react: 19.2.7 + lz-string@1.5.0: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -18947,14 +22621,40 @@ snapshots: socks: 2.8.7 optional: true - motion-dom@12.23.23: - dependencies: - motion-utils: 12.23.6 - - motion-utils@12.23.6: {} - - ms@2.1.3: {} - + motion-dom@12.23.23: + dependencies: + motion-utils: 12.23.6 + + motion-utils@12.23.6: {} + + ms@2.1.3: {} + + msw@2.12.10(@types/node@22.20.0)(typescript@6.0.3): + dependencies: + '@inquirer/confirm': 5.1.21(@types/node@22.20.0) + '@mswjs/interceptors': 0.41.3 + '@open-draft/deferred-promise': 2.2.0 + '@types/statuses': 2.0.6 + cookie: 1.1.1 + graphql: 16.13.1 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + rettime: 0.10.1 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.1 + type-fest: 5.4.4 + until-async: 3.0.2 + yargs: 17.7.2 + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - '@types/node' + optional: true + msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3): dependencies: '@inquirer/confirm': 5.1.21(@types/node@24.12.0) @@ -19049,7 +22749,7 @@ snapshots: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - next@16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + next@16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: '@next/env': 16.0.10 '@swc/helpers': 0.5.15 @@ -19057,7 +22757,7 @@ snapshots: postcss: 8.4.31 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.7) + styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.7) optionalDependencies: '@next/swc-darwin-arm64': 16.0.10 '@next/swc-darwin-x64': 16.0.10 @@ -19103,6 +22803,62 @@ snapshots: - babel-plugin-macros optional: true + nf3@0.3.19: {} + + nitro@3.0.260603-beta(@electric-sql/pglite@0.3.15)(@vercel/blob@0.27.3)(chokidar@4.0.3)(dotenv@17.2.3)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(rollup@4.53.2)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): + dependencies: + consola: 3.4.2 + crossws: 0.4.6(srvx@0.11.16) + db0: 0.3.4(@electric-sql/pglite@0.3.15)(mysql2@3.15.3) + env-runner: 0.1.16 + h3: 2.0.1-rc.22(crossws@0.4.6(srvx@0.11.16)) + hookable: 6.1.1 + nf3: 0.3.19 + ocache: 0.1.5 + ofetch: 2.0.0-alpha.3 + ohash: 2.0.11 + rolldown: 1.1.0 + srvx: 0.11.16 + unenv: 2.0.0-rc.24 + unstorage: 2.0.0-alpha.7(@vercel/blob@0.27.3)(chokidar@4.0.3)(db0@0.3.4(@electric-sql/pglite@0.3.15)(mysql2@3.15.3))(lru-cache@11.5.1)(mongodb@6.21.0(socks@2.8.7))(ofetch@2.0.0-alpha.3) + optionalDependencies: + dotenv: 17.2.3 + giget: 2.0.0 + jiti: 2.7.0 + rollup: 4.53.2 + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@netlify/runtime' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - better-sqlite3 + - chokidar + - drizzle-orm + - idb-keyval + - ioredis + - lru-cache + - miniflare + - mongodb + - mysql2 + - sqlite3 + - uploadthing + - wrangler + no-case@2.3.2: dependencies: lower-case: 1.1.4 @@ -19154,13 +22910,13 @@ snapshots: dependencies: boolbase: 1.0.0 - nuqs@2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7): + nuqs@2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7): dependencies: '@standard-schema/spec': 1.0.0 react: 19.2.7 optionalDependencies: '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - next: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react-router: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) nypm@0.6.2: @@ -19217,6 +22973,14 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + obug@2.1.3: {} + + ocache@0.1.5: + dependencies: + ohash: 2.0.11 + + ofetch@2.0.0-alpha.3: {} + ohash@2.0.11: {} on-finished@2.4.1: @@ -19314,6 +23078,34 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + oxc-parser@0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + dependencies: + '@oxc-project/types': 0.120.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.120.0 + '@oxc-parser/binding-android-arm64': 0.120.0 + '@oxc-parser/binding-darwin-arm64': 0.120.0 + '@oxc-parser/binding-darwin-x64': 0.120.0 + '@oxc-parser/binding-freebsd-x64': 0.120.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.120.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.120.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.120.0 + '@oxc-parser/binding-linux-arm64-musl': 0.120.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.120.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.120.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.120.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.120.0 + '@oxc-parser/binding-linux-x64-gnu': 0.120.0 + '@oxc-parser/binding-linux-x64-musl': 0.120.0 + '@oxc-parser/binding-openharmony-arm64': 0.120.0 + '@oxc-parser/binding-wasm32-wasi': 0.120.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@oxc-parser/binding-win32-arm64-msvc': 0.120.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.120.0 + '@oxc-parser/binding-win32-x64-msvc': 0.120.0 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): optionalDependencies: '@oxc-resolver/binding-android-arm-eabi': 11.19.1 @@ -19424,12 +23216,10 @@ snapshots: dependencies: domhandler: 5.0.3 parse5: 7.3.0 - optional: true parse5-parser-stream@7.1.2: dependencies: parse5: 7.3.0 - optional: true parse5@7.3.0: dependencies: @@ -19438,7 +23228,6 @@ snapshots: parse5@8.0.1: dependencies: entities: 8.0.0 - optional: true parseurl@1.3.3: {} @@ -19509,7 +23298,7 @@ snapshots: pkg-types@2.3.1: dependencies: confbox: 0.2.4 - exsolve: 1.0.8 + exsolve: 1.1.0 pathe: 2.0.3 optional: true @@ -19529,14 +23318,14 @@ snapshots: postcss-selector-parser: 7.1.0 postcss-value-parser: 4.2.0 - postcss-cli@11.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.22.4): + postcss-cli@11.0.1(jiti@2.7.0)(postcss@8.5.6)(tsx@4.22.4): dependencies: chokidar: 3.6.0 dependency-graph: 1.0.0 fs-extra: 11.3.2 picocolors: 1.1.1 postcss: 8.5.6 - postcss-load-config: 5.1.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.22.4) + postcss-load-config: 5.1.0(jiti@2.7.0)(postcss@8.5.6)(tsx@4.22.4) postcss-reporter: 7.1.0(postcss@8.5.6) pretty-hrtime: 1.0.3 read-cache: 1.0.0 @@ -19578,12 +23367,12 @@ snapshots: dependencies: postcss: 8.5.6 - postcss-load-config@5.1.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.22.4): + postcss-load-config@5.1.0(jiti@2.7.0)(postcss@8.5.6)(tsx@4.22.4): dependencies: lilconfig: 3.1.3 yaml: 2.8.2 optionalDependencies: - jiti: 2.6.1 + jiti: 2.7.0 postcss: 8.5.6 tsx: 4.22.4 @@ -19741,11 +23530,20 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.8.4: - optional: true + prettier-plugin-tailwindcss@0.8.0(prettier@3.8.4): + dependencies: + prettier: 3.8.4 + + prettier@3.8.4: {} pretty-bytes@7.1.0: {} + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + pretty-hrtime@1.0.3: {} pretty-ms@9.3.0: @@ -19769,6 +23567,23 @@ snapshots: - react-dom optional: true + prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3): + dependencies: + '@prisma/config': 7.5.0 + '@prisma/dev': 0.20.0(typescript@6.0.3) + '@prisma/engines': 7.5.0 + '@prisma/studio-core': 0.21.1(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + mysql2: 3.15.3 + postgres: 3.4.7 + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - '@types/react' + - magicast + - react + - react-dom + optional: true + prismjs@1.30.0: {} prompts@2.4.2: @@ -20010,6 +23825,69 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) + radix-ui@1.6.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-accessible-icon': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-accordion': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-alert-dialog': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-aspect-ratio': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-avatar': 1.2.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-checkbox': 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collapsible': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-context-menu': 2.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dialog': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dropdown-menu': 2.1.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-form': 0.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-hover-card': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-label': 2.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-menu': 2.1.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-menubar': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-navigation-menu': 1.2.17(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-one-time-password-field': 0.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-password-toggle-field': 0.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popover': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-progress': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-radio-group': 1.4.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-scroll-area': 1.2.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-select': 2.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-separator': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slider': 1.4.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-switch': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tabs': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toast': 1.2.18(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle-group': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toolbar': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tooltip': 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-escape-keydown': 1.1.3(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.14)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + range-parser@1.2.1: {} raw-body@3.0.2: @@ -20063,6 +23941,8 @@ snapshots: react-is@16.13.1: {} + react-is@17.0.2: {} + react-markdown@9.1.0(@types/react@19.2.14)(react@19.2.7): dependencies: '@types/hast': 3.0.4 @@ -20086,6 +23966,8 @@ snapshots: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) + react-refresh@0.18.0: {} + react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.7): dependencies: react: 19.2.7 @@ -20105,6 +23987,17 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + react-remove-scroll@2.7.2(@types/react@19.2.14)(react@19.2.7): + dependencies: + react: 19.2.7 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.14)(react@19.2.7) + react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.7) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.14)(react@19.2.7) + use-sidecar: 1.1.3(@types/react@19.2.14)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + react-resizable-panels@2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: react: 19.2.7 @@ -20117,7 +24010,6 @@ snapshots: set-cookie-parser: 2.7.2 optionalDependencies: react-dom: 19.2.7(react@19.2.7) - optional: true react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.2.7): dependencies: @@ -20413,7 +24305,6 @@ snapshots: '@rolldown/binding-wasm32-wasi': 1.1.0 '@rolldown/binding-win32-arm64-msvc': 1.1.0 '@rolldown/binding-win32-x64-msvc': 1.1.0 - optional: true rollup-plugin-dts@6.2.3(rollup@4.53.2)(typescript@5.9.3): dependencies: @@ -20476,8 +24367,7 @@ snapshots: rou3@0.7.12: {} - rou3@0.8.1: - optional: true + rou3@0.8.1: {} router@2.2.0: dependencies: @@ -20533,7 +24423,6 @@ snapshots: saxes@6.0.0: dependencies: xmlchars: 2.2.0 - optional: true scheduler@0.27.0: {} @@ -20549,8 +24438,7 @@ snapshots: semver@7.7.3: {} - semver@7.8.4: - optional: true + semver@7.8.4: {} send@1.2.1: dependencies: @@ -20579,10 +24467,8 @@ snapshots: seroval-plugins@1.5.4(seroval@1.5.4): dependencies: seroval: 1.5.4 - optional: true - seroval@1.5.4: - optional: true + seroval@1.5.4: {} serve-static@2.2.1: dependencies: @@ -20593,8 +24479,7 @@ snapshots: transitivePeerDependencies: - supports-color - set-cookie-parser@2.7.2: - optional: true + set-cookie-parser@2.7.2: {} set-cookie-parser@3.1.0: {} @@ -20665,6 +24550,46 @@ snapshots: - supports-color - typescript + shadcn@4.13.0(typescript@6.0.3): + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.7) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.7) + '@dotenvx/dotenvx': 1.59.1 + '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) + '@types/validate-npm-package-name': 4.0.2 + browserslist: 4.28.2 + commander: 14.0.3 + cosmiconfig: 9.0.1(typescript@6.0.3) + dedent: 1.7.0 + deepmerge: 4.3.1 + diff: 8.0.4 + execa: 9.6.1 + fast-glob: 3.3.3 + fs-extra: 11.3.2 + fuzzysort: 3.1.0 + kleur: 4.1.5 + open: 11.0.0 + ora: 8.2.0 + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + prompts: 2.4.2 + recast: 0.23.11 + stringify-object: 5.0.0 + tailwind-merge: 3.6.0 + ts-morph: 26.0.0 + tsconfig-paths: 4.2.0 + undici: 7.27.2 + validate-npm-package-name: 7.0.2 + zod: 4.4.3 + zod-to-json-schema: 3.25.2(zod@4.4.3) + transitivePeerDependencies: + - '@cfworker/json-schema' + - babel-plugin-macros + - supports-color + - typescript + sharp@0.34.5: dependencies: '@img/colour': 1.0.0 @@ -20703,6 +24628,8 @@ snapshots: shebang-regex@3.0.0: {} + shell-quote@1.9.0: {} + shiki@3.15.0: dependencies: '@shikijs/core': 3.15.0 @@ -20782,7 +24709,6 @@ snapshots: csstype: 3.2.3 seroval: 1.5.4 seroval-plugins: 1.5.4(seroval@1.5.4) - optional: true sonner@2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: @@ -20805,11 +24731,11 @@ snapshots: sqlstring@2.3.3: optional: true - srvx@0.11.16: - optional: true + srvx@0.11.16: {} - stable-hash-x@0.2.0: - optional: true + srvx@0.11.20: {} + + stable-hash-x@0.2.0: {} stable-hash@0.0.5: {} @@ -20819,6 +24745,8 @@ snapshots: std-env@3.10.0: {} + std-env@4.1.0: {} + stdin-discarder@0.2.2: {} stop-iteration-iterator@1.1.0: @@ -20941,20 +24869,12 @@ snapshots: dependencies: inline-style-parser: 0.2.7 - styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.7): - dependencies: - client-only: 0.0.1 - react: 19.2.7 - optionalDependencies: - '@babel/core': 7.29.0 - styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.7): dependencies: client-only: 0.0.1 react: 19.2.7 optionalDependencies: '@babel/core': 7.29.7 - optional: true stylehacks@7.0.7(postcss@8.5.6): dependencies: @@ -20993,8 +24913,7 @@ snapshots: react: 19.2.7 use-sync-external-store: 1.6.0(react@19.2.7) - symbol-tree@3.2.4: - optional: true + symbol-tree@3.2.4: {} tabbable@6.4.0: {} @@ -21006,8 +24925,12 @@ snapshots: tailwindcss@4.2.2: {} + tailwindcss@4.3.2: {} + tapable@2.3.0: {} + tapable@2.3.3: {} + text-table@0.2.0: {} thenby@1.3.4: {} @@ -21045,6 +24968,8 @@ snapshots: tinyrainbow@2.0.0: {} + tinyrainbow@3.1.0: {} + tinyspy@4.0.4: {} title-case@2.1.1: @@ -21080,7 +25005,6 @@ snapshots: tr46@6.0.0: dependencies: punycode: 2.3.1 - optional: true trim-lines@3.0.1: {} @@ -21090,6 +25014,15 @@ snapshots: dependencies: typescript: 5.9.3 + ts-api-utils@2.5.0(typescript@6.0.3): + dependencies: + typescript: 6.0.3 + + ts-declaration-location@1.0.7(typescript@6.0.3): + dependencies: + picomatch: 4.0.4 + typescript: 6.0.3 + ts-morph@26.0.0: dependencies: '@ts-morph/common': 0.27.0 @@ -21118,6 +25051,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 @@ -21147,7 +25084,6 @@ snapshots: esbuild: 0.28.0 optionalDependencies: fsevents: 2.3.3 - optional: true turbo-darwin-64@2.6.1: optional: true @@ -21229,14 +25165,26 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 + typescript-eslint@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + eslint: 9.39.4(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + typescript@5.9.3: {} + typescript@6.0.3: {} + uc.micro@2.1.0: {} ufo@1.6.1: {} - ufo@1.6.4: - optional: true + ufo@1.6.4: {} uglify-js@3.19.3: optional: true @@ -21296,8 +25244,11 @@ snapshots: dependencies: '@fastify/busboy': 2.1.1 - undici@7.27.2: - optional: true + undici@7.27.2: {} + + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 unicorn-magic@0.3.0: {} @@ -21358,7 +25309,6 @@ snapshots: acorn: 8.16.0 picomatch: 4.0.4 webpack-virtual-modules: 0.6.2 - optional: true unrs-resolver@1.11.1: dependencies: @@ -21410,7 +25360,15 @@ snapshots: '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2 '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 - optional: true + + unstorage@2.0.0-alpha.7(@vercel/blob@0.27.3)(chokidar@4.0.3)(db0@0.3.4(@electric-sql/pglite@0.3.15)(mysql2@3.15.3))(lru-cache@11.5.1)(mongodb@6.21.0(socks@2.8.7))(ofetch@2.0.0-alpha.3): + optionalDependencies: + '@vercel/blob': 0.27.3 + chokidar: 4.0.3 + db0: 0.3.4(@electric-sql/pglite@0.3.15)(mysql2@3.15.3) + lru-cache: 11.5.1 + mongodb: 6.21.0(socks@2.8.7) + ofetch: 2.0.0-alpha.3 until-async@3.0.2: {} @@ -21481,6 +25439,11 @@ snapshots: typescript: 5.9.3 optional: true + valibot@1.2.0(typescript@6.0.3): + optionalDependencies: + typescript: 6.0.3 + optional: true + validate-npm-package-name@5.0.1: {} validate-npm-package-name@7.0.2: {} @@ -21511,13 +25474,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-node@3.2.4(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.2.4(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -21532,13 +25495,13 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2): + vite-node@3.2.4(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + vite: 7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -21553,13 +25516,13 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.2.4(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -21574,7 +25537,34 @@ snapshots: - tsx - yaml - vite@7.3.1(@types/node@24.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2): + vite-tsconfig-paths@5.1.4(typescript@6.0.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@6.0.3) + optionalDependencies: + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + - typescript + + vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.6 + rollup: 4.53.2 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 22.20.0 + fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + tsx: 4.22.4 + yaml: 2.8.2 + + vite@7.3.1(@types/node@24.0.3)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) @@ -21585,13 +25575,13 @@ snapshots: optionalDependencies: '@types/node': 24.0.3 fsevents: 2.3.3 - jiti: 2.6.1 + jiti: 2.7.0 lightningcss: 1.32.0 tsx: 4.22.4 yaml: 2.8.2 optional: true - vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): + vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) @@ -21602,12 +25592,12 @@ snapshots: optionalDependencies: '@types/node': 24.12.0 fsevents: 2.3.3 - jiti: 2.6.1 + jiti: 2.7.0 lightningcss: 1.32.0 tsx: 4.21.0 yaml: 2.8.2 - vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2): + vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) @@ -21618,12 +25608,12 @@ snapshots: optionalDependencies: '@types/node': 24.12.0 fsevents: 2.3.3 - jiti: 2.6.1 + jiti: 2.7.0 lightningcss: 1.32.0 tsx: 4.22.4 yaml: 2.8.2 - vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): + vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) @@ -21634,21 +25624,25 @@ snapshots: optionalDependencies: '@types/node': 25.5.0 fsevents: 2.3.3 - jiti: 2.6.1 + jiti: 2.7.0 lightningcss: 1.32.0 tsx: 4.21.0 yaml: 2.8.2 - vitefu@1.1.3(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)): + vitefu@1.1.3(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): + optionalDependencies: + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + + vitefu@1.1.3(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)): optionalDependencies: - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) optional: true - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -21666,8 +25660,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -21687,11 +25681,11 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.22.4)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.22.4)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -21709,8 +25703,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + vite: 7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -21730,11 +25724,11 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -21752,8 +25746,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -21773,6 +25767,47 @@ snapshots: - tsx - yaml + vitest@4.1.9(@opentelemetry/api@1.9.0)(@types/node@22.20.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@22.20.0)(typescript@6.0.3))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): + dependencies: + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(msw@2.12.10(@types/node@22.20.0)(typescript@6.0.3))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + es-module-lexer: 2.3.0 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.3 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 22.20.0 + jsdom: 28.1.0(@noble/hashes@2.0.1) + transitivePeerDependencies: + - msw + + vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@2.7.0)): + dependencies: + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.6.0 + semver: 7.8.4 + transitivePeerDependencies: + - supports-color + vue@3.5.24(typescript@5.9.3): dependencies: '@vue/compiler-dom': 3.5.24 @@ -21783,12 +25818,22 @@ snapshots: optionalDependencies: typescript: 5.9.3 + vue@3.5.24(typescript@6.0.3): + dependencies: + '@vue/compiler-dom': 3.5.24 + '@vue/compiler-sfc': 3.5.24 + '@vue/runtime-dom': 3.5.24 + '@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@6.0.3)) + '@vue/shared': 3.5.24 + optionalDependencies: + typescript: 6.0.3 + optional: true + w3c-keyname@2.2.8: {} w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 - optional: true walk-up-path@4.0.0: {} @@ -21803,22 +25848,17 @@ snapshots: webidl-conversions@7.0.0: optional: true - webidl-conversions@8.0.1: - optional: true + webidl-conversions@8.0.1: {} - webpack-virtual-modules@0.6.2: - optional: true + webpack-virtual-modules@0.6.2: {} whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 - optional: true - whatwg-mimetype@4.0.0: - optional: true + whatwg-mimetype@4.0.0: {} - whatwg-mimetype@5.0.0: - optional: true + whatwg-mimetype@5.0.0: {} whatwg-url@14.2.0: dependencies: @@ -21833,7 +25873,6 @@ snapshots: webidl-conversions: 8.0.1 transitivePeerDependencies: - '@noble/hashes' - optional: true which-boxed-primitive@1.1.1: dependencies: @@ -21907,13 +25946,14 @@ snapshots: wrappy@1.0.2: {} + ws@8.21.0: {} + wsl-utils@0.3.1: dependencies: is-wsl: 3.1.1 powershell-utils: 0.1.0 - xml-name-validator@5.0.0: - optional: true + xml-name-validator@5.0.0: {} xmlbuilder2@4.0.3: dependencies: @@ -21921,10 +25961,8 @@ snapshots: '@oozcitak/infra': 2.0.2 '@oozcitak/util': 10.0.0 js-yaml: 4.2.0 - optional: true - xmlchars@2.2.0: - optional: true + xmlchars@2.2.0: {} y18n@5.0.8: {} @@ -21962,8 +26000,7 @@ snapshots: dependencies: zod: 4.4.3 - zod@3.25.76: - optional: true + zod@3.25.76: {} zod@4.4.3: {} diff --git a/scripts/codegen/files/nextjs/app/cms-example/page.tsx b/scripts/codegen/files/nextjs/app/cms-example/page.tsx index 7106f74f6..0b4ba9685 100644 --- a/scripts/codegen/files/nextjs/app/cms-example/page.tsx +++ b/scripts/codegen/files/nextjs/app/cms-example/page.tsx @@ -6,9 +6,7 @@ import { } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; import { QueryClientProvider } from "@tanstack/react-query"; -import { useRouter } from "next/navigation"; import { useState } from "react"; -import Link from "next/link"; import Image from "next/image"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; import { getOrCreateQueryClient } from "@/lib/query-client"; @@ -29,35 +27,6 @@ async function mockUploadFile(file: File): Promise { return "https://example-files.online-convert.com/document/txt/example.txt"; } -// Shared Next.js Image wrapper -function NextImageWrapper(props: React.ImgHTMLAttributes) { - const { alt = "", src = "", width, height, ...rest } = props; - - if (!width || !height) { - return ( - - {alt} - - ); - } - - return ( - {alt} - ); -} - type PluginOverrides = { cms: CMSPluginOverrides; }; @@ -240,7 +209,6 @@ function CMSExampleContent() { } export default function CMSExamplePage() { - const router = useRouter(); const [queryClient] = useState(() => getOrCreateQueryClient()); const baseURL = getBaseURL(); @@ -248,17 +216,11 @@ export default function CMSExamplePage() { basePath="/cms-example" + router={nextRouter()} + api={{ baseURL, basePath: "/api/data" }} overrides={{ cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => router.push(path), - refresh: () => router.refresh(), uploadImage: mockUploadFile, - Link: ({ href, ...props }) => ( - - ), - Image: NextImageWrapper, }, }} > diff --git a/scripts/codegen/files/nextjs/app/directory/[id]/page.tsx b/scripts/codegen/files/nextjs/app/directory/[id]/page.tsx index a62bd644a..7f22215fe 100644 --- a/scripts/codegen/files/nextjs/app/directory/[id]/page.tsx +++ b/scripts/codegen/files/nextjs/app/directory/[id]/page.tsx @@ -3,7 +3,7 @@ import { useContentItemPopulated } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; import { QueryClientProvider } from "@tanstack/react-query"; -import { useRouter, useParams } from "next/navigation"; +import { useParams } from "next/navigation"; import { useState } from "react"; import Link from "next/link"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; @@ -153,7 +153,6 @@ function ResourceDetailContent({ id }: { id: string }) { } export default function ResourceDetailPage() { - const router = useRouter(); const params = useParams(); const [queryClient] = useState(() => getOrCreateQueryClient()); const baseURL = getBaseURL(); @@ -164,17 +163,8 @@ export default function ResourceDetailPage() { basePath="/directory" - overrides={{ - cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => router.push(path), - refresh: () => router.refresh(), - Link: ({ href, ...props }) => ( - - ), - }, - }} + router={nextRouter()} + api={{ baseURL, basePath: "/api/data" }} > diff --git a/scripts/codegen/files/nextjs/app/directory/category/[categoryId]/page.tsx b/scripts/codegen/files/nextjs/app/directory/category/[categoryId]/page.tsx index c220b6e3a..e0f7e6a80 100644 --- a/scripts/codegen/files/nextjs/app/directory/category/[categoryId]/page.tsx +++ b/scripts/codegen/files/nextjs/app/directory/category/[categoryId]/page.tsx @@ -6,7 +6,7 @@ import { } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; import { QueryClientProvider } from "@tanstack/react-query"; -import { useRouter, useParams } from "next/navigation"; +import { useParams } from "next/navigation"; import { useState } from "react"; import Link from "next/link"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; @@ -160,7 +160,6 @@ function CategoryContent({ categoryId }: { categoryId: string }) { } export default function CategoryPage() { - const router = useRouter(); const params = useParams(); const [queryClient] = useState(() => getOrCreateQueryClient()); const baseURL = getBaseURL(); @@ -171,17 +170,8 @@ export default function CategoryPage() { basePath="/directory" - overrides={{ - cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => router.push(path), - refresh: () => router.refresh(), - Link: ({ href, ...props }) => ( - - ), - }, - }} + router={nextRouter()} + api={{ baseURL, basePath: "/api/data" }} > diff --git a/scripts/codegen/files/nextjs/app/directory/page.tsx b/scripts/codegen/files/nextjs/app/directory/page.tsx index ae112aa43..d123a129b 100644 --- a/scripts/codegen/files/nextjs/app/directory/page.tsx +++ b/scripts/codegen/files/nextjs/app/directory/page.tsx @@ -3,7 +3,6 @@ import { useContent } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; import { QueryClientProvider } from "@tanstack/react-query"; -import { useRouter } from "next/navigation"; import { useState, useMemo } from "react"; import Link from "next/link"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; @@ -176,7 +175,6 @@ function DirectoryContent() { } export default function DirectoryPage() { - const router = useRouter(); const [queryClient] = useState(() => getOrCreateQueryClient()); const baseURL = getBaseURL(); @@ -184,17 +182,8 @@ export default function DirectoryPage() { basePath="/directory" - overrides={{ - cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => router.push(path), - refresh: () => router.refresh(), - Link: ({ href, ...props }) => ( - - ), - }, - }} + router={nextRouter()} + api={{ baseURL, basePath: "/api/data" }} > diff --git a/scripts/codegen/files/nextjs/app/pages/layout.tsx b/scripts/codegen/files/nextjs/app/pages/layout.tsx index 1b7ad3008..2ace5f6fb 100644 --- a/scripts/codegen/files/nextjs/app/pages/layout.tsx +++ b/scripts/codegen/files/nextjs/app/pages/layout.tsx @@ -1,14 +1,12 @@ "use client"; import React, { useState } from "react"; import { StackProvider } from "@btst/stack/context"; +import { nextRouter } from "@btst/stack/next"; import { QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; -import Link from "next/link"; -import Image from "next/image"; -import { useRouter } from "next/navigation"; import type { TodosPluginOverrides } from "@/lib/plugins/todo/client/overrides"; import { getOrCreateQueryClient } from "@/lib/query-client"; -import { BlogPluginOverrides } from "@btst/stack/plugins/blog/client"; +import type { BlogPluginOverrides } from "@btst/stack/plugins/blog/client"; import type { AiChatPluginOverrides } from "@btst/stack/plugins/ai-chat/client"; import { ChatLayout } from "@btst/stack/plugins/ai-chat/client"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; @@ -37,37 +35,6 @@ const getBaseURL = () => ? process.env.NEXT_PUBLIC_BASE_URL || window.location.origin : process.env.BASE_URL || "http://localhost:3000"; -// Shared Next.js Image wrapper for plugins -// Handles both cases: with explicit dimensions or using fill mode -function NextImageWrapper(props: React.ImgHTMLAttributes) { - const { alt = "", src = "", width, height, ...rest } = props; - - // Use fill mode if width or height are not provided - if (!width || !height) { - return ( - - {alt} - - ); - } - - return ( - {alt} - ); -} - // Define the shape of all plugin overrides type PluginOverrides = { todos: TodosPluginOverrides; @@ -86,7 +53,6 @@ export default function ExampleLayout({ }: { children: React.ReactNode; }) { - const router = useRouter(); // fresh instance to avoid stale client cache overriding hydrated data const [queryClient] = useState(() => getOrCreateQueryClient()); const baseURL = getBaseURL(); @@ -125,22 +91,16 @@ export default function ExampleLayout({ basePath="/pages" + router={nextRouter()} + api={{ baseURL, basePath: "/api/data" }} overrides={{ - todos: { - Link: (props: React.ComponentProps) => { - return ; - }, - navigate: (path) => router.push(path), - }, + // Only genuinely plugin-specific overrides remain — the shared + // Link/navigate/refresh/Image and API wiring come from the + // top-level `router` and `api` props above. blog: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => router.push(path), - refresh: () => router.refresh(), uploadImage, imagePicker: ImagePicker, imageInputField: ImageInputField, - Image: NextImageWrapper, // Wire comments into the bottom of each blog post postBottomSlot: (post) => ( ), - // Lifecycle Hooks - called during route rendering - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onRouteRender: Route rendered:`, - routeName, - context.path, - ); - }, - onRouteError: async (routeName, error, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onRouteError: Route error:`, - routeName, - error.message, - context.path, - ); - }, - onBeforePostsPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforePostsPageRendered: checking access for`, - context.path, - ); - return true; - }, - onBeforeDraftsPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeDraftsPageRendered: checking auth for`, - context.path, - ); - return true; - }, - onBeforeNewPostPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeNewPostPageRendered: checking permissions for`, - context.path, - ); - return true; - }, - onBeforeEditPostPageRendered: (slug, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeEditPostPageRendered: checking permissions for`, - slug, - context.path, - ); - return true; - }, - onBeforePostPageRendered: (slug, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforePostPageRendered: checking access for`, - slug, - context.path, - ); - return true; - }, }, "ai-chat": { mode: "authenticated", // Full chat with conversation history - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => router.push(path), - refresh: () => router.refresh(), uploadFile: uploadFileForChat, - Link: ({ href, ...props }) => ( - - ), - Image: NextImageWrapper, chatSuggestions: [ "Hi, I'm Sarah, 34. I'm getting married next year and I just inherited $50,000 from my grandmother. I have no debt and about $30k in savings. I'm wondering if my current moderate-risk portfolio still makes sense.", "Hi, I run a small import business and want to invest $200,000. The money came from overseas sales across multiple countries over the past few months. I'd like to move it into Canadian equities right away.", @@ -226,93 +125,16 @@ export default function ExampleLayout({ "I'm approaching retirement in the next few years — what should I be thinking about?", "How is my risk tolerance assessed?", ], - // Lifecycle hooks - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] AI Chat route:`, - routeName, - context.path, - ); - }, - onRouteError: async (routeName, error, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] AI Chat error:`, - routeName, - error.message, - ); - }, }, cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => router.push(path), - refresh: () => router.refresh(), uploadImage, imagePicker: ImagePicker, imageInputField: ImageInputField, - Link: ({ href, ...props }) => ( - - ), - Image: NextImageWrapper, - // Lifecycle hooks - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] CMS route:`, - routeName, - context.path, - ); - }, - onRouteError: async (routeName, error, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] CMS error:`, - routeName, - error.message, - ); - }, - }, - "form-builder": { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => router.push(path), - refresh: () => router.refresh(), - Link: ({ href, ...props }) => ( - - ), - // Lifecycle hooks - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] Form Builder route:`, - routeName, - context.path, - ); - }, - onRouteError: async (routeName, error, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] Form Builder error:`, - routeName, - error.message, - ); - }, }, "ui-builder": { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => router.push(path), - refresh: () => router.refresh(), - Link: ({ href, ...props }) => ( - - ), componentRegistry: defaultComponentRegistry, }, kanban: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => router.push(path), - refresh: () => router.refresh(), - Link: ({ href, ...props }) => ( - - ), - uploadImage, imagePicker: ImagePicker, // User resolution for assignees @@ -330,65 +152,18 @@ export default function ExampleLayout({ loginHref="/login" /> ), - // Lifecycle hooks - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] Kanban route:`, - routeName, - context.path, - ); - }, - onRouteError: async (routeName, error, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] Kanban error:`, - routeName, - error.message, - ); - }, - onBeforeBoardsPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeBoardsPageRendered`, - ); - return true; - }, - onBeforeBoardPageRendered: (boardId, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeBoardPageRendered:`, - boardId, - ); - return true; - }, }, comments: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", // In production: derive from your auth session currentUserId: "olliethedev", defaultCommentPageSize: 5, resourceLinks: { "blog-post": (slug) => `/pages/blog/${slug}`, }, - onBeforeModerationPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeModerationPageRendered`, - ); - return true; // In production: check admin role - }, - onBeforeUserCommentsPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeUserCommentsPageRendered`, - ); - return true; // In production: check authenticated session - }, }, media: { ...mediaClientConfig, queryClient, - navigate: (path) => router.push(path), - Link: ({ href, ...props }) => ( - - ), - Image: NextImageWrapper, }, }} > diff --git a/scripts/codegen/files/react-router/app/routes/cms-example.tsx b/scripts/codegen/files/react-router/app/routes/cms-example.tsx index b346c12b3..c94660846 100644 --- a/scripts/codegen/files/react-router/app/routes/cms-example.tsx +++ b/scripts/codegen/files/react-router/app/routes/cms-example.tsx @@ -3,7 +3,6 @@ import { useContent, } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; -import { Link, useNavigate } from "react-router"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; import type { CMSTypes } from "../lib/cms-schemas"; @@ -185,23 +184,16 @@ function CMSExampleContent() { } export default function CMSExamplePage() { - const navigate = useNavigate(); const baseURL = getBaseURL(); return ( basePath="/cms-example" + router={reactRouter()} + api={{ baseURL, basePath: "/api/data" }} overrides={{ cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => navigate(href), uploadImage: mockUploadFile, - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), }, }} > diff --git a/scripts/codegen/files/react-router/app/routes/directory/category.$categoryId.tsx b/scripts/codegen/files/react-router/app/routes/directory/category.$categoryId.tsx index 0a5570ccb..cca1b7f49 100644 --- a/scripts/codegen/files/react-router/app/routes/directory/category.$categoryId.tsx +++ b/scripts/codegen/files/react-router/app/routes/directory/category.$categoryId.tsx @@ -3,7 +3,8 @@ import { useContentByRelation, } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; -import { Link, useNavigate, useParams } from "react-router"; +import { reactRouter } from "@btst/stack/react-router"; +import { Link, useParams } from "react-router"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; import type { CMSTypes } from "../../lib/cms-schemas"; import { ArrowLeft } from "lucide-react"; @@ -152,7 +153,6 @@ function CategoryContent({ categoryId }: { categoryId: string }) { } export default function CategoryPage() { - const navigate = useNavigate(); const params = useParams(); const baseURL = getBaseURL(); const categoryId = params.categoryId as string; @@ -160,18 +160,8 @@ export default function CategoryPage() { return ( basePath="/directory" - overrides={{ - cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => navigate(path), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - }, - }} + router={reactRouter()} + api={{ baseURL, basePath: "/api/data" }} > diff --git a/scripts/codegen/files/react-router/app/routes/directory/index.tsx b/scripts/codegen/files/react-router/app/routes/directory/index.tsx index f140bb3d5..436dccc23 100644 --- a/scripts/codegen/files/react-router/app/routes/directory/index.tsx +++ b/scripts/codegen/files/react-router/app/routes/directory/index.tsx @@ -1,6 +1,7 @@ import { useContent } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; -import { Link, useNavigate } from "react-router"; +import { reactRouter } from "@btst/stack/react-router"; +import { Link } from "react-router"; import { useState, useMemo } from "react"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; import type { CMSTypes } from "../../lib/cms-schemas"; @@ -170,24 +171,13 @@ function DirectoryContent() { } export default function DirectoryPage() { - const navigate = useNavigate(); const baseURL = getBaseURL(); return ( basePath="/directory" - overrides={{ - cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => navigate(path), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - }, - }} + router={reactRouter()} + api={{ baseURL, basePath: "/api/data" }} > diff --git a/scripts/codegen/files/react-router/app/routes/directory/resource.$id.tsx b/scripts/codegen/files/react-router/app/routes/directory/resource.$id.tsx index 7d15b95f6..ac3d49151 100644 --- a/scripts/codegen/files/react-router/app/routes/directory/resource.$id.tsx +++ b/scripts/codegen/files/react-router/app/routes/directory/resource.$id.tsx @@ -1,6 +1,7 @@ import { useContentItemPopulated } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; -import { Link, useNavigate, useParams } from "react-router"; +import { reactRouter } from "@btst/stack/react-router"; +import { Link, useParams } from "react-router"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; import type { CMSTypes } from "../../lib/cms-schemas"; import { ArrowLeft, ExternalLink } from "lucide-react"; @@ -146,7 +147,6 @@ function ResourceDetailContent({ id }: { id: string }) { } export default function ResourceDetailPage() { - const navigate = useNavigate(); const params = useParams(); const baseURL = getBaseURL(); const id = params.id as string; @@ -154,18 +154,8 @@ export default function ResourceDetailPage() { return ( basePath="/directory" - overrides={{ - cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => navigate(path), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - }, - }} + router={reactRouter()} + api={{ baseURL, basePath: "/api/data" }} > diff --git a/scripts/codegen/files/react-router/app/routes/pages/_layout.tsx b/scripts/codegen/files/react-router/app/routes/pages/_layout.tsx index 5aeb23d46..8621b9c6e 100644 --- a/scripts/codegen/files/react-router/app/routes/pages/_layout.tsx +++ b/scripts/codegen/files/react-router/app/routes/pages/_layout.tsx @@ -1,6 +1,7 @@ import { useCallback, useMemo, useState } from "react"; -import { Outlet, Link, useNavigate, useRevalidator } from "react-router"; +import { Outlet } from "react-router"; import { StackProvider } from "@btst/stack/context"; +import { reactRouter } from "@btst/stack/react-router"; import type { BlogPluginOverrides } from "@btst/stack/plugins/blog/client"; import type { AiChatPluginOverrides } from "@btst/stack/plugins/ai-chat/client"; import { ChatLayout } from "@btst/stack/plugins/ai-chat/client"; @@ -47,8 +48,6 @@ type PluginOverrides = { export default function Layout() { const baseURL = getBaseURL(); - const navigate = useNavigate(); - const { revalidate } = useRevalidator(); const [queryClient] = useState(() => getOrCreateQueryClient()); const mediaClientConfig = useMemo( () => ({ @@ -83,91 +82,19 @@ export default function Layout() { return ( basePath="/pages" + router={reactRouter()} + api={{ baseURL, basePath: "/api/data" }} overrides={{ - todos: { - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - navigate: (href) => navigate(href), - }, + // Only genuinely plugin-specific overrides remain — the shared + // Link/navigate/refresh and API wiring come from the top-level + // `router` and `api` props above. "ui-builder": { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => navigate(href), - refresh: () => revalidate(), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), componentRegistry: defaultComponentRegistry, }, blog: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => navigate(href), uploadImage, imagePicker: ImagePicker, imageInputField: ImageInputField, - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onRouteRender: Route rendered:`, - routeName, - context.path, - ); - }, - onRouteError: async (routeName, error, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onRouteError: Route error:`, - routeName, - error.message, - context.path, - ); - }, - onBeforePostsPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforePostsPageRendered: checking access for`, - context.path, - ); - return true; - }, - onBeforeDraftsPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeDraftsPageRendered: checking auth for`, - context.path, - ); - return true; - }, - onBeforeNewPostPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeNewPostPageRendered: checking permissions for`, - context.path, - ); - return true; - }, - onBeforeEditPostPageRendered: (slug, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeEditPostPageRendered: checking permissions for`, - slug, - context.path, - ); - return true; - }, - onBeforePostPageRendered: (slug, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforePostPageRendered: checking access for`, - slug, - context.path, - ); - return true; - }, // Wire comments into the bottom of each blog post postBottomSlot: (post) => ( navigate(href), uploadFile: uploadFileForChat, - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] AI Chat route:`, - routeName, - context.path, - ); - }, }, cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => navigate(href), uploadImage, imagePicker: ImagePicker, imageInputField: ImageInputField, - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] CMS route:`, - routeName, - context.path, - ); - }, - }, - "form-builder": { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => navigate(href), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] Form Builder route:`, - routeName, - context.path, - ); - }, - onRouteError: async (routeName, error, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] Form Builder error:`, - routeName, - error.message, - ); - }, }, kanban: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => navigate(href), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), uploadImage, imagePicker: ImagePicker, resolveUser, @@ -270,44 +135,17 @@ export default function Layout() { loginHref="/login" /> ), - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] Kanban route:`, - routeName, - context.path, - ); - }, }, comments: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", currentUserId: "olliethedev", defaultCommentPageSize: 5, resourceLinks: { "blog-post": (slug) => `/pages/blog/${slug}`, }, - onBeforeModerationPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeModerationPageRendered`, - ); - return true; - }, - onBeforeUserCommentsPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeUserCommentsPageRendered`, - ); - return true; - }, }, media: { ...mediaClientConfig, queryClient, - navigate: (href) => navigate(href), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), }, }} > diff --git a/scripts/codegen/files/tanstack/src/routes/cms-example.tsx b/scripts/codegen/files/tanstack/src/routes/cms-example.tsx index 0ef01521f..dbd278ef2 100644 --- a/scripts/codegen/files/tanstack/src/routes/cms-example.tsx +++ b/scripts/codegen/files/tanstack/src/routes/cms-example.tsx @@ -3,8 +3,9 @@ import { useContent, } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; +import { tanstackRouter } from "@btst/stack/tanstack"; import { QueryClientProvider } from "@tanstack/react-query"; -import { Link, useRouter, createFileRoute } from "@tanstack/react-router"; +import { createFileRoute } from "@tanstack/react-router"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; import type { CMSTypes } from "@/lib/cms-schemas"; @@ -190,7 +191,6 @@ function CMSExampleContent() { } function CMSExamplePage() { - const router = useRouter(); const context = Route.useRouteContext(); const baseURL = getBaseURL(); @@ -198,17 +198,11 @@ function CMSExamplePage() { basePath="/cms-example" + router={tanstackRouter()} + api={{ baseURL, basePath: "/api/data" }} overrides={{ cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => router.navigate({ href }), uploadImage: mockUploadFile, - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), }, }} > diff --git a/scripts/codegen/files/tanstack/src/routes/directory/$id.tsx b/scripts/codegen/files/tanstack/src/routes/directory/$id.tsx index ec2d75a0a..ba8383fa6 100644 --- a/scripts/codegen/files/tanstack/src/routes/directory/$id.tsx +++ b/scripts/codegen/files/tanstack/src/routes/directory/$id.tsx @@ -1,12 +1,8 @@ import { useContentItemPopulated } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; +import { tanstackRouter } from "@btst/stack/tanstack"; import { QueryClientProvider } from "@tanstack/react-query"; -import { - Link, - useRouter, - createFileRoute, - useParams, -} from "@tanstack/react-router"; +import { Link, createFileRoute, useParams } from "@tanstack/react-router"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; import type { CMSTypes } from "@/lib/cms-schemas"; import { ArrowLeft, ExternalLink } from "lucide-react"; @@ -149,7 +145,6 @@ function ResourceDetailContent({ id }: { id: string }) { } function ResourceDetailPage() { - const router = useRouter(); const context = Route.useRouteContext(); const { id } = useParams({ from: "/directory/$id" }); const baseURL = getBaseURL(); @@ -158,18 +153,8 @@ function ResourceDetailPage() { basePath="/directory" - overrides={{ - cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => router.navigate({ href }), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - }, - }} + router={tanstackRouter()} + api={{ baseURL, basePath: "/api/data" }} > diff --git a/scripts/codegen/files/tanstack/src/routes/directory/category/$categoryId.tsx b/scripts/codegen/files/tanstack/src/routes/directory/category/$categoryId.tsx index bae6c4801..e273534ee 100644 --- a/scripts/codegen/files/tanstack/src/routes/directory/category/$categoryId.tsx +++ b/scripts/codegen/files/tanstack/src/routes/directory/category/$categoryId.tsx @@ -3,13 +3,9 @@ import { useContentByRelation, } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; +import { tanstackRouter } from "@btst/stack/tanstack"; import { QueryClientProvider } from "@tanstack/react-query"; -import { - Link, - useRouter, - createFileRoute, - useParams, -} from "@tanstack/react-router"; +import { Link, createFileRoute, useParams } from "@tanstack/react-router"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; import type { CMSTypes } from "@/lib/cms-schemas"; import { ArrowLeft } from "lucide-react"; @@ -160,7 +156,6 @@ function CategoryContent({ categoryId }: { categoryId: string }) { } function CategoryPage() { - const router = useRouter(); const context = Route.useRouteContext(); const { categoryId } = useParams({ from: "/directory/category/$categoryId" }); const baseURL = getBaseURL(); @@ -169,18 +164,8 @@ function CategoryPage() { basePath="/directory" - overrides={{ - cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => router.navigate({ href }), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - }, - }} + router={tanstackRouter()} + api={{ baseURL, basePath: "/api/data" }} > diff --git a/scripts/codegen/files/tanstack/src/routes/directory/index.tsx b/scripts/codegen/files/tanstack/src/routes/directory/index.tsx index 1f8b0a663..ad67bd67d 100644 --- a/scripts/codegen/files/tanstack/src/routes/directory/index.tsx +++ b/scripts/codegen/files/tanstack/src/routes/directory/index.tsx @@ -1,7 +1,8 @@ import { useContent } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; +import { tanstackRouter } from "@btst/stack/tanstack"; import { QueryClientProvider } from "@tanstack/react-query"; -import { Link, useRouter, createFileRoute } from "@tanstack/react-router"; +import { Link, createFileRoute } from "@tanstack/react-router"; import { useState, useMemo } from "react"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; import type { CMSTypes } from "@/lib/cms-schemas"; @@ -167,7 +168,6 @@ function DirectoryContent() { } function DirectoryPage() { - const router = useRouter(); const context = Route.useRouteContext(); const baseURL = getBaseURL(); @@ -175,18 +175,8 @@ function DirectoryPage() { basePath="/directory" - overrides={{ - cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => router.navigate({ href }), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - }, - }} + router={tanstackRouter()} + api={{ baseURL, basePath: "/api/data" }} > diff --git a/scripts/codegen/files/tanstack/src/routes/pages/route.tsx b/scripts/codegen/files/tanstack/src/routes/pages/route.tsx index db5a6aacc..709e8784a 100644 --- a/scripts/codegen/files/tanstack/src/routes/pages/route.tsx +++ b/scripts/codegen/files/tanstack/src/routes/pages/route.tsx @@ -1,4 +1,5 @@ import { StackProvider } from "@btst/stack/context"; +import { tanstackRouter } from "@btst/stack/tanstack"; import { QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { useCallback, useMemo } from "react"; @@ -20,12 +21,7 @@ import { } from "@btst/stack/plugins/media/client/components"; import { Button } from "../../components/ui/button"; import { resolveUser, searchUsers } from "../../lib/mock-users"; -import { - Link, - useRouter, - Outlet, - createFileRoute, -} from "@tanstack/react-router"; +import { Outlet, createFileRoute } from "@tanstack/react-router"; import type { TodosPluginOverrides } from "../../lib/plugins/todo/client/overrides"; import type { UIBuilderPluginOverrides } from "@btst/stack/plugins/ui-builder/client"; import { defaultComponentRegistry } from "@btst/stack/plugins/ui-builder/client"; @@ -59,7 +55,6 @@ export const Route = createFileRoute("/pages")({ }); function Layout() { - const router = useRouter(); const routeContext = Route.useRouteContext(); const baseURL = getBaseURL(); const mediaClientConfig = useMemo( @@ -97,91 +92,19 @@ function Layout() { basePath="/pages" + router={tanstackRouter()} + api={{ baseURL, basePath: "/api/data" }} overrides={{ - todos: { - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - navigate: (href) => router.navigate({ href }), - }, + // Only genuinely plugin-specific overrides remain — the shared + // Link/navigate/refresh and API wiring come from the top-level + // `router` and `api` props above. "ui-builder": { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => router.navigate({ href }), - refresh: () => router.invalidate(), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), componentRegistry: defaultComponentRegistry, }, blog: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => router.navigate({ href }), uploadImage, imagePicker: ImagePicker, imageInputField: ImageInputField, - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onRouteRender: Route rendered:`, - routeName, - context.path, - ); - }, - onRouteError: async (routeName, error, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onRouteError: Route error:`, - routeName, - error.message, - context.path, - ); - }, - onBeforePostsPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforePostsPageRendered: checking access for`, - context.path, - ); - return true; - }, - onBeforeDraftsPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeDraftsPageRendered: checking auth for`, - context.path, - ); - return true; - }, - onBeforeNewPostPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeNewPostPageRendered: checking permissions for`, - context.path, - ); - return true; - }, - onBeforeEditPostPageRendered: (slug, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeEditPostPageRendered: checking permissions for`, - slug, - context.path, - ); - return true; - }, - onBeforePostPageRendered: (slug, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforePostPageRendered: checking access for`, - slug, - context.path, - ); - return true; - }, // Wire comments into the bottom of each blog post postBottomSlot: (post) => ( router.navigate({ href }), uploadFile: uploadFileForChat, - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] AI Chat route:`, - routeName, - context.path, - ); - }, }, cms: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => router.navigate({ href }), uploadImage, imagePicker: ImagePicker, imageInputField: ImageInputField, - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] CMS route:`, - routeName, - context.path, - ); - }, - }, - "form-builder": { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => router.navigate({ href }), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] Form Builder route:`, - routeName, - context.path, - ); - }, - onRouteError: async (routeName, error, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] Form Builder error:`, - routeName, - error.message, - ); - }, }, kanban: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => router.navigate({ href }), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), uploadImage, imagePicker: ImagePicker, resolveUser, @@ -284,44 +145,17 @@ function Layout() { loginHref="/login" /> ), - onRouteRender: async (routeName, context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] Kanban route:`, - routeName, - context.path, - ); - }, }, comments: { - apiBaseURL: baseURL, - apiBasePath: "/api/data", currentUserId: "olliethedev", defaultCommentPageSize: 5, resourceLinks: { "blog-post": (slug) => `/pages/blog/${slug}`, }, - onBeforeModerationPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeModerationPageRendered`, - ); - return true; - }, - onBeforeUserCommentsPageRendered: (context) => { - console.log( - `[${context.isSSR ? "SSR" : "CSR"}] onBeforeUserCommentsPageRendered`, - ); - return true; - }, }, media: { ...mediaClientConfig, queryClient: routeContext.queryClient, - navigate: (href) => router.navigate({ href }), - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), }, }} > From 9f54ba7932874b8c626f4faaff1fea19f96d6f6f Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Fri, 3 Jul 2026 22:21:00 +0000 Subject: [PATCH 007/380] fix: restore router preset imports stripped by editor import cleanup Co-authored-by: Cursor --- scripts/codegen/files/nextjs/app/cms-example/page.tsx | 1 + scripts/codegen/files/nextjs/app/directory/[id]/page.tsx | 1 + .../files/nextjs/app/directory/category/[categoryId]/page.tsx | 1 + scripts/codegen/files/nextjs/app/directory/page.tsx | 1 + scripts/codegen/files/react-router/app/routes/cms-example.tsx | 1 + 5 files changed, 5 insertions(+) diff --git a/scripts/codegen/files/nextjs/app/cms-example/page.tsx b/scripts/codegen/files/nextjs/app/cms-example/page.tsx index 0b4ba9685..5b51ca4e4 100644 --- a/scripts/codegen/files/nextjs/app/cms-example/page.tsx +++ b/scripts/codegen/files/nextjs/app/cms-example/page.tsx @@ -5,6 +5,7 @@ import { useContent, } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; +import { nextRouter } from "@btst/stack/next"; import { QueryClientProvider } from "@tanstack/react-query"; import { useState } from "react"; import Image from "next/image"; diff --git a/scripts/codegen/files/nextjs/app/directory/[id]/page.tsx b/scripts/codegen/files/nextjs/app/directory/[id]/page.tsx index 7f22215fe..c05ad3be7 100644 --- a/scripts/codegen/files/nextjs/app/directory/[id]/page.tsx +++ b/scripts/codegen/files/nextjs/app/directory/[id]/page.tsx @@ -2,6 +2,7 @@ import { useContentItemPopulated } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; +import { nextRouter } from "@btst/stack/next"; import { QueryClientProvider } from "@tanstack/react-query"; import { useParams } from "next/navigation"; import { useState } from "react"; diff --git a/scripts/codegen/files/nextjs/app/directory/category/[categoryId]/page.tsx b/scripts/codegen/files/nextjs/app/directory/category/[categoryId]/page.tsx index e0f7e6a80..82c183176 100644 --- a/scripts/codegen/files/nextjs/app/directory/category/[categoryId]/page.tsx +++ b/scripts/codegen/files/nextjs/app/directory/category/[categoryId]/page.tsx @@ -5,6 +5,7 @@ import { useContentByRelation, } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; +import { nextRouter } from "@btst/stack/next"; import { QueryClientProvider } from "@tanstack/react-query"; import { useParams } from "next/navigation"; import { useState } from "react"; diff --git a/scripts/codegen/files/nextjs/app/directory/page.tsx b/scripts/codegen/files/nextjs/app/directory/page.tsx index d123a129b..c711487a8 100644 --- a/scripts/codegen/files/nextjs/app/directory/page.tsx +++ b/scripts/codegen/files/nextjs/app/directory/page.tsx @@ -2,6 +2,7 @@ import { useContent } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; +import { nextRouter } from "@btst/stack/next"; import { QueryClientProvider } from "@tanstack/react-query"; import { useState, useMemo } from "react"; import Link from "next/link"; diff --git a/scripts/codegen/files/react-router/app/routes/cms-example.tsx b/scripts/codegen/files/react-router/app/routes/cms-example.tsx index c94660846..703817721 100644 --- a/scripts/codegen/files/react-router/app/routes/cms-example.tsx +++ b/scripts/codegen/files/react-router/app/routes/cms-example.tsx @@ -3,6 +3,7 @@ import { useContent, } from "@btst/stack/plugins/cms/client/hooks"; import { StackProvider } from "@btst/stack/context"; +import { reactRouter } from "@btst/stack/react-router"; import type { CMSPluginOverrides } from "@btst/stack/plugins/cms/client"; import type { CMSTypes } from "../lib/cms-schemas"; From 1c564b7010e2e0c7e9e450146311074daa837dca Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sat, 4 Jul 2026 00:26:54 +0000 Subject: [PATCH 008/380] feat: framework entry factories for page handlers and API routes (#130) Ship createNextPage/createReactRouterPage/createTanStackPageOptions and toNextRouteHandlers/toReactRouterHandlers/toTanStackHandlers in the per-framework entry points from #127, so each catch-all page and API route file collapses to a few lines. Factories own SSR prefetch, dehydration (including failed queries), loader-before-meta ordering, and 404 handling; hand-written glue keeps working. Updates all three codegen templates and rewrites the two installation doc steps with the manual versions kept in advanced accordions. Co-authored-by: Cursor --- docs/content/docs/installation.mdx | 410 +++++++++++------- .../cli/src/templates/nextjs/api-route.ts.hbs | 7 +- .../src/templates/nextjs/pages-route.tsx.hbs | 48 +- .../templates/react-router/api-route.ts.hbs | 10 +- .../react-router/pages-route.tsx.hbs | 47 +- .../src/templates/tanstack/api-route.ts.hbs | 11 +- .../templates/tanstack/pages-route.tsx.hbs | 35 +- .../src/utils/__tests__/scaffold-plan.test.ts | 6 +- .../src/__tests__/entry-factories.test.tsx | 258 +++++++++++ packages/stack/src/next/handlers.ts | 24 + packages/stack/src/next/index.tsx | 113 +---- packages/stack/src/next/page.tsx | 95 ++++ packages/stack/src/next/router.tsx | 102 +++++ packages/stack/src/react-router/handlers.ts | 21 + packages/stack/src/react-router/index.tsx | 74 +--- packages/stack/src/react-router/page.tsx | 105 +++++ packages/stack/src/react-router/router.tsx | 63 +++ packages/stack/src/shared/entry-factories.ts | 55 +++ packages/stack/src/tanstack/handlers.ts | 28 ++ packages/stack/src/tanstack/index.tsx | 71 +-- packages/stack/src/tanstack/page.tsx | 88 ++++ packages/stack/src/tanstack/router.tsx | 61 +++ .../files/tanstack/src/routes/pages/$.tsx | 37 +- 23 files changed, 1207 insertions(+), 562 deletions(-) create mode 100644 packages/stack/src/__tests__/entry-factories.test.tsx create mode 100644 packages/stack/src/next/handlers.ts create mode 100644 packages/stack/src/next/page.tsx create mode 100644 packages/stack/src/next/router.tsx create mode 100644 packages/stack/src/react-router/handlers.ts create mode 100644 packages/stack/src/react-router/page.tsx create mode 100644 packages/stack/src/react-router/router.tsx create mode 100644 packages/stack/src/shared/entry-factories.ts create mode 100644 packages/stack/src/tanstack/handlers.ts create mode 100644 packages/stack/src/tanstack/page.tsx create mode 100644 packages/stack/src/tanstack/router.tsx diff --git a/docs/content/docs/installation.mdx b/docs/content/docs/installation.mdx index e197a4b37..3ff2ad02d 100644 --- a/docs/content/docs/installation.mdx +++ b/docs/content/docs/installation.mdx @@ -6,6 +6,7 @@ description: Learn how to install and configure BTST in your project. import { Steps, Step } from "fumadocs-ui/components/steps"; import { Tabs, Tab } from "fumadocs-ui/components/tabs"; import { Callout } from "fumadocs-ui/components/callout"; +import { Accordions, Accordion } from "fumadocs-ui/components/accordion"; ## AI Agent Skills @@ -358,61 +359,35 @@ In order to use BTST, your application must meet the following requirements: ### Create API Route - Create a catch-all API route to handle BTST requests. The route will handle requests for the path `/api/data/*`. If you use a different path make sure to update the `basePath` in the `stack` config to match your chosen path. + Create a catch-all API route to handle BTST requests. The `toNextRouteHandlers` / `toReactRouterHandlers` / `toTanStackHandlers` helpers from the framework entry points wire your stack `handler` to every HTTP method the route needs. The route will handle requests for the path `/api/data/*`. If you use a different path make sure to update the `basePath` in the `stack` config to match your chosen path. ```ts title="app/api/data/[[...all]]/route.ts" + import { toNextRouteHandlers } from "@btst/stack/next" import { handler } from "@/lib/stack" - export const GET = handler - export const POST = handler - export const PUT = handler - export const PATCH = handler - export const DELETE = handler + export const { GET, POST, PUT, PATCH, DELETE } = toNextRouteHandlers(handler) ``` - ```ts title="app/routes/api/data/route.ts" + ```ts title="app/routes/api/data/$.ts" + import { toReactRouterHandlers } from "@btst/stack/react-router" import { handler } from "~/lib/stack" - import type { LoaderFunctionArgs, ActionFunctionArgs } from "@remix-run/node" - export async function loader({ request }: LoaderFunctionArgs) { - return handler(request) - } - - export async function action({ request }: ActionFunctionArgs) { - return handler(request) - } + export const { loader, action } = toReactRouterHandlers(handler) ``` ```ts title="src/routes/api/data/$.ts" - import { createFileRoute } from '@tanstack/react-router' - import { handler } from '@/lib/stack' + import { createFileRoute } from "@tanstack/react-router" + import { toTanStackHandlers } from "@btst/stack/tanstack" + import { handler } from "@/lib/stack" - export const Route = createFileRoute('/api/data/$')({ - server: { - handlers: { - GET: async ({ request }) => { - return handler(request) - }, - POST: async ({ request }) => { - return handler(request) - }, - PUT: async ({ request }) => { - return handler(request) - }, - PATCH: async ({ request }) => { - return handler(request) - }, - DELETE: async ({ request }) => { - return handler(request) - }, - }, - }, + export const Route = createFileRoute("/api/data/$")({ + server: { handlers: toTanStackHandlers(handler) }, }) ``` @@ -455,6 +430,60 @@ In order to use BTST, your application must meet the following requirements: ``` + + + + The helpers only wire your `handler` to each HTTP method — hand-writing the glue remains fully supported if you need custom behavior (auth wrappers, logging, filtering methods): + + + + ```ts title="app/api/data/[[...all]]/route.ts" + import { handler } from "@/lib/stack" + + export const GET = handler + export const POST = handler + export const PUT = handler + export const PATCH = handler + export const DELETE = handler + ``` + + + + ```ts title="app/routes/api/data/$.ts" + import type { Route } from "./+types/$" + import { handler } from "~/lib/stack" + + export function loader({ request }: Route.LoaderArgs) { + return handler(request) + } + + export function action({ request }: Route.ActionArgs) { + return handler(request) + } + ``` + + + + ```ts title="src/routes/api/data/$.ts" + import { createFileRoute } from '@tanstack/react-router' + import { handler } from '@/lib/stack' + + export const Route = createFileRoute('/api/data/$')({ + server: { + handlers: { + GET: async ({ request }) => handler(request), + POST: async ({ request }) => handler(request), + PUT: async ({ request }) => handler(request), + PATCH: async ({ request }) => handler(request), + DELETE: async ({ request }) => handler(request), + }, + }, + }) + ``` + + + + @@ -782,162 +811,213 @@ In order to use BTST, your application must meet the following requirements: ### Set Up Page Handler - Create a catch-all route to handle BTST pages defined in your plugins. This enables server-side rendering, metadata generation and automatic route handling. + Create a catch-all route to handle BTST pages defined in your plugins. The page factories from the framework entry points own the invariant plumbing once: server-side prefetching via `route.loader()`, React Query dehydration (including failed queries, so the client doesn't refetch on errors), loader-before-meta ordering for SEO metadata, and 404 handling via your framework's mechanism. ```tsx title="app/pages/[[...all]]/page.tsx" - import { dehydrate, HydrationBoundary } from "@tanstack/react-query" - import { notFound } from "next/navigation" + import { createNextPage } from "@btst/stack/next" import { getOrCreateQueryClient } from "@/lib/query-client" import { getStackClient } from "@/lib/stack-client" - import { metaElementsToObject, normalizePath } from "@btst/stack/client" - import { Metadata } from "next" - export default async function Page({ params }: { params: Promise<{ all: string[] }> }) { - const pathParams = await params - const path = normalizePath(pathParams?.all) - - const queryClient = getOrCreateQueryClient() - const stackClient = getStackClient(queryClient) - const route = stackClient.router.getRoute(path) - - // Prefetch data server-side if the route has a loader - if (route?.loader) await route.loader() - - // Serialize React Query cache for client hydration - const dehydratedState = dehydrate(queryClient) - - return ( - - {route && route.PageComponent ? : notFound()} - - ) - } + export const dynamic = "force-dynamic" - export async function generateMetadata({ params }: { params: Promise<{ all: string[] }> }) { - const pathParams = await params - const path = normalizePath(pathParams?.all) - - const queryClient = getOrCreateQueryClient() - const stackClient = getStackClient(queryClient) - const route = stackClient.router.getRoute(path) - - if (!route) return notFound() - if (route?.loader) await route.loader() - - // Convert plugin meta elements to Next.js Metadata format - return route.meta ? metaElementsToObject(route.meta()) satisfies Metadata : { title: "No meta" } - } + const page = createNextPage({ getStackClient, getQueryClient: getOrCreateQueryClient }) + export default page.Page + export const generateMetadata = page.generateMetadata ``` - ```tsx title="app/routes/pages/index.tsx" - import type { Route } from "./+types/index" - import { useLoaderData } from "react-router" - import { dehydrate, HydrationBoundary, QueryClient, useQueryClient } from "@tanstack/react-query" + ```tsx title="app/routes/pages/$.tsx" + import { createReactRouterPage } from "@btst/stack/react-router" + import { getOrCreateQueryClient } from "~/lib/query-client" import { getStackClient } from "~/lib/stack-client" - import { normalizePath } from "@btst/stack/client" - - export async function loader({ params }: Route.LoaderArgs) { - const path = normalizePath(params["*"]) - - // Create QueryClient for this request with consistent config - const queryClient = new QueryClient({ - defaultOptions: { queries: { staleTime: 1000 * 60 * 5, refetchOnMount: false, retry: false } } - }) - const stackClient = getStackClient(queryClient) - const route = stackClient.router.getRoute(path) - - if (route?.loader) await route.loader() - - // Include errors so client doesn't refetch on error - const dehydratedState = dehydrate(queryClient) - - return { path, dehydratedState, meta: route?.meta?.() } - } - export function meta({ loaderData }: Route.MetaArgs) { - return loaderData.meta - } - - export default function PagesIndex() { - const { path, dehydratedState } = useLoaderData() - const queryClient = useQueryClient() - const route = getStackClient(queryClient).router.getRoute(path) - const Page = route && route.PageComponent ? :
Route not found
- - return dehydratedState ? ( - {Page} - ) : Page - } + const page = createReactRouterPage({ getStackClient, getQueryClient: getOrCreateQueryClient }) + export const loader = page.loader + export const meta = page.meta + export const ErrorBoundary = page.ErrorBoundary + export default page.Component ```
```tsx title="src/routes/pages/$.tsx" - import { createFileRoute, notFound } from "@tanstack/react-router" + import { createFileRoute } from "@tanstack/react-router" + import { createTanStackPageOptions } from "@btst/stack/tanstack" import { getStackClient } from "@/lib/stack-client" - import { normalizePath } from "@btst/stack/client" - - export const Route = createFileRoute("/pages/$")({ - ssr: true, - component: Page, - loader: async ({ params, context }) => { - const routePath = normalizePath(params._splat) - const stackClient = getStackClient(context.queryClient) - const route = stackClient.router.getRoute(routePath) - - if (!route) throw notFound() - if (route?.loader) await route.loader() - - return { meta: await route?.meta?.() } - }, - head: ({ loaderData }) => { - return loaderData?.meta && Array.isArray(loaderData.meta) - ? { meta: loaderData.meta } - : { meta: [{ title: "No Meta" }], title: "No Meta" } - }, - notFoundComponent: () =>

This page doesn't exist!

- }) - function Page() { - const context = Route.useRouteContext() - const { _splat } = Route.useParams() - const routePath = normalizePath(_splat) - const route = getStackClient(context.queryClient).router.getRoute(routePath) - - return route && route.PageComponent ? :
Route not found
- } + export const Route = createFileRoute("/pages/$")( + createTanStackPageOptions({ getStackClient }), + ) ```
- - **How it works:** - - `stackClient.router.getRoute(path)` matches the URL to a plugin route and returns a route object: - - ```typescript - route = { - PageComponent: React.ComponentType, // The page to render - loader?: () => Promise, // Prefetches React Query data - meta?: () => MetadataElements, // Returns SEO metadata - ErrorComponent?: React.ComponentType, // Standalone error components - LoadingComponent?: React.ComponentType // Standalone loading components - } - ``` - - **Key steps:** - - **Server-side data loading**: Call `route.loader()` before rendering to prefetch data into React Query cache - - **Hydration**: Use `dehydrate()` to serialize prefetched data for the client (not required with TanStack Start) - - **Error handling**: Configure your query client with `shouldDehydrateQuery` to include failed queries in dehydration, preventing client-side refetching on errors - - **Metadata generation**: Use `route.meta()` with framework-specific meta functions for SEO - - **404 handling**: Return `notFound()` or your framework's equivalent function when routes don't exist + **How it works:** + + The factory matches the URL to a plugin route via `stackClient.router.getRoute(path)`, prefetches data server-side with `route.loader()`, renders the route's `PageComponent` with instant hydration on the client, and generates SEO metadata from `route.meta()` (running the loader first, so meta can read prefetched data). + + **Escape hatches:** + - `createNextPage` accepts `notFound` (replaces `notFound()` from `next/navigation`) and `wrapPage` + - `createReactRouterPage` accepts `NotFound` (component rendered when no route matches) and `wrapPage` + - `createTanStackPageOptions` accepts `getQueryClient` (defaults to the router context's `queryClient`); spread extra route options like `notFoundComponent` alongside it + - Hand-writing the route file remains fully supported — see below + + + `stackClient.router.getRoute(path)` matches the URL to a plugin route and returns a route object: + + ```typescript + route = { + PageComponent: React.ComponentType, // The page to render + loader?: () => Promise, // Prefetches React Query data + meta?: () => MetadataElements, // Returns SEO metadata + ErrorComponent?: React.ComponentType, // Standalone error components + LoadingComponent?: React.ComponentType // Standalone loading components + } + ``` + + If you need behavior the factories don't cover, write the catch-all route yourself: + + + + ```tsx title="app/pages/[[...all]]/page.tsx" + import { dehydrate, HydrationBoundary } from "@tanstack/react-query" + import { notFound } from "next/navigation" + import { getOrCreateQueryClient } from "@/lib/query-client" + import { getStackClient } from "@/lib/stack-client" + import { metaElementsToObject, normalizePath } from "@btst/stack/client" + import { Metadata } from "next" + + export default async function Page({ params }: { params: Promise<{ all: string[] }> }) { + const pathParams = await params + const path = normalizePath(pathParams?.all) + + const queryClient = getOrCreateQueryClient() + const stackClient = getStackClient(queryClient) + const route = stackClient.router.getRoute(path) + + // Prefetch data server-side if the route has a loader + if (route?.loader) await route.loader() + + // Serialize React Query cache for client hydration + const dehydratedState = dehydrate(queryClient) + + return ( + + {route && route.PageComponent ? : notFound()} + + ) + } + + export async function generateMetadata({ params }: { params: Promise<{ all: string[] }> }) { + const pathParams = await params + const path = normalizePath(pathParams?.all) + + const queryClient = getOrCreateQueryClient() + const stackClient = getStackClient(queryClient) + const route = stackClient.router.getRoute(path) + + if (!route) return notFound() + if (route?.loader) await route.loader() + + // Convert plugin meta elements to Next.js Metadata format + return route.meta ? metaElementsToObject(route.meta()) satisfies Metadata : { title: "No meta" } + } + ``` + + + + ```tsx title="app/routes/pages/$.tsx" + import type { Route } from "./+types/$" + import { useLoaderData } from "react-router" + import { dehydrate, HydrationBoundary, useQueryClient } from "@tanstack/react-query" + import { getOrCreateQueryClient } from "~/lib/query-client" + import { getStackClient } from "~/lib/stack-client" + import { normalizePath } from "@btst/stack/client" + + export async function loader({ params }: Route.LoaderArgs) { + const queryClient = getOrCreateQueryClient() + const path = normalizePath(params["*"]) + const route = getStackClient(queryClient).router.getRoute(path) + + if (route?.loader) await route.loader() + + // Include errors so client doesn't refetch on error + const dehydratedState = dehydrate(queryClient) + + return { path, dehydratedState, meta: route?.meta?.() } + } + + export function meta({ loaderData }: Route.MetaArgs) { + return loaderData.meta + } + + export default function PagesIndex() { + const { path, dehydratedState } = useLoaderData() + const queryClient = useQueryClient() + const route = getStackClient(queryClient).router.getRoute(path) + const Page = route && route.PageComponent ? :
Route not found
+ + return dehydratedState ? ( + {Page} + ) : Page + } + ``` +
+ + + ```tsx title="src/routes/pages/$.tsx" + import { createFileRoute, notFound } from "@tanstack/react-router" + import { getStackClient } from "@/lib/stack-client" + import { normalizePath } from "@btst/stack/client" + + export const Route = createFileRoute("/pages/$")({ + ssr: true, + component: Page, + loader: async ({ params, context }) => { + const routePath = normalizePath(params._splat) + const stackClient = getStackClient(context.queryClient) + const route = stackClient.router.getRoute(routePath) + + if (!route) throw notFound() + if (route?.loader) await route.loader() + + return { meta: await route?.meta?.() } + }, + head: ({ loaderData }) => { + return loaderData?.meta && Array.isArray(loaderData.meta) + ? { meta: loaderData.meta } + : { meta: [{ title: "No Meta" }], title: "No Meta" } + }, + notFoundComponent: () =>

This page doesn't exist!

+ }) + + function Page() { + const context = Route.useRouteContext() + const { _splat } = Route.useParams() + const routePath = normalizePath(_splat) + const route = getStackClient(context.queryClient).router.getRoute(routePath) + + return route && route.PageComponent ? :
Route not found
+ } + ``` +
+
+ + **Key steps to get right by hand:** + - **Server-side data loading**: Call `route.loader()` before rendering to prefetch data into React Query cache + - **Hydration**: Use `dehydrate()` to serialize prefetched data for the client (not required with TanStack Start) + - **Error handling**: Configure your query client with `shouldDehydrateQuery` to include failed queries in dehydration, preventing client-side refetching on errors + - **Metadata generation**: Use `route.meta()` with framework-specific meta functions for SEO + - **404 handling**: Return `notFound()` or your framework's equivalent function when routes don't exist +
+
+
diff --git a/packages/cli/src/templates/nextjs/api-route.ts.hbs b/packages/cli/src/templates/nextjs/api-route.ts.hbs index de220aca1..7f535e300 100644 --- a/packages/cli/src/templates/nextjs/api-route.ts.hbs +++ b/packages/cli/src/templates/nextjs/api-route.ts.hbs @@ -1,7 +1,4 @@ +import { toNextRouteHandlers } from "@btst/stack/next" import { handler } from "{{alias}}lib/stack" -export const GET = handler -export const POST = handler -export const PUT = handler -export const PATCH = handler -export const DELETE = handler +export const { GET, POST, PUT, PATCH, DELETE } = toNextRouteHandlers(handler) diff --git a/packages/cli/src/templates/nextjs/pages-route.tsx.hbs b/packages/cli/src/templates/nextjs/pages-route.tsx.hbs index 638c69bc1..6fc4b56d2 100644 --- a/packages/cli/src/templates/nextjs/pages-route.tsx.hbs +++ b/packages/cli/src/templates/nextjs/pages-route.tsx.hbs @@ -1,49 +1,9 @@ -import { dehydrate, HydrationBoundary } from "@tanstack/react-query" -import { normalizePath, metaElementsToObject } from "@btst/stack/client" -import { notFound } from "next/navigation" +import { createNextPage } from "@btst/stack/next" import { getOrCreateQueryClient } from "{{alias}}lib/query-client" import { getStackClient } from "{{alias}}lib/stack-client" -import type { Metadata } from "next" export const dynamic = "force-dynamic" -export default async function BtstPagesRoute({ - params, -}: { - params: Promise<{ all?: string[] }> -}) { - const pathParams = await params - const path = normalizePath(pathParams?.all) - const queryClient = getOrCreateQueryClient() - const stackClient = getStackClient(queryClient) - const route = stackClient.router.getRoute(path) - - if (route?.loader) { - await route.loader() - } - - return ( - - {route?.PageComponent ? : notFound()} - - ) -} - -export async function generateMetadata({ - params, -}: { - params: Promise<{ all?: string[] }> -}): Promise { - const pathParams = await params - const path = normalizePath(pathParams?.all) - const queryClient = getOrCreateQueryClient() - const stackClient = getStackClient(queryClient) - const route = stackClient.router.getRoute(path) - if (!route?.meta) { - return {} - } - if (route?.loader) { - await route.loader() - } - return metaElementsToObject(route.meta()) satisfies Metadata -} +const page = createNextPage({ getStackClient, getQueryClient: getOrCreateQueryClient }) +export default page.Page +export const generateMetadata = page.generateMetadata diff --git a/packages/cli/src/templates/react-router/api-route.ts.hbs b/packages/cli/src/templates/react-router/api-route.ts.hbs index 09bc3c5ec..ad62a8446 100644 --- a/packages/cli/src/templates/react-router/api-route.ts.hbs +++ b/packages/cli/src/templates/react-router/api-route.ts.hbs @@ -1,10 +1,4 @@ -import type { Route } from "./+types/$" +import { toReactRouterHandlers } from "@btst/stack/react-router" import { handler } from "{{alias}}lib/stack" -export function loader({ request }: Route.LoaderArgs) { - return handler(request) -} - -export function action({ request }: Route.ActionArgs) { - return handler(request) -} +export const { loader, action } = toReactRouterHandlers(handler) diff --git a/packages/cli/src/templates/react-router/pages-route.tsx.hbs b/packages/cli/src/templates/react-router/pages-route.tsx.hbs index f0e562d3f..bc46b43b2 100644 --- a/packages/cli/src/templates/react-router/pages-route.tsx.hbs +++ b/packages/cli/src/templates/react-router/pages-route.tsx.hbs @@ -1,44 +1,9 @@ -import type { Route } from "./+types/$" -import { useLoaderData, useRouteError } from "react-router" -import { dehydrate, HydrationBoundary, useQueryClient } from "@tanstack/react-query" -import { normalizePath } from "@btst/stack/client" +import { createReactRouterPage } from "@btst/stack/react-router" import { getOrCreateQueryClient } from "{{alias}}lib/query-client" import { getStackClient } from "{{alias}}lib/stack-client" -export async function loader({ params }: Route.LoaderArgs) { - const queryClient = getOrCreateQueryClient() - const path = normalizePath(params["*"]) - const route = getStackClient(queryClient).router.getRoute(path) - - if (route?.loader) { - await route.loader() - } - - return { - path, - dehydratedState: dehydrate(queryClient), - meta: route?.meta?.(), - } -} - -export function meta({ loaderData }: Route.MetaArgs) { - return loaderData.meta -} - -export default function BtstPagesRoute() { - const data = useLoaderData() - const queryClient = useQueryClient() - const route = getStackClient(queryClient).router.getRoute(data.path) - const page = route?.PageComponent ? :
Route not found
- - return ( - - {page} - - ) -} - -export function ErrorBoundary() { - const error = useRouteError() - return
{String(error)}
-} +const page = createReactRouterPage({ getStackClient, getQueryClient: getOrCreateQueryClient }) +export const loader = page.loader +export const meta = page.meta +export const ErrorBoundary = page.ErrorBoundary +export default page.Component diff --git a/packages/cli/src/templates/tanstack/api-route.ts.hbs b/packages/cli/src/templates/tanstack/api-route.ts.hbs index 79aeedf11..4d6307c25 100644 --- a/packages/cli/src/templates/tanstack/api-route.ts.hbs +++ b/packages/cli/src/templates/tanstack/api-route.ts.hbs @@ -1,14 +1,7 @@ import { createFileRoute } from "@tanstack/react-router" +import { toTanStackHandlers } from "@btst/stack/tanstack" import { handler } from "{{alias}}lib/stack" export const Route = createFileRoute("/api/data/$")({ - server: { - handlers: { - GET: async ({ request }) => handler(request), - POST: async ({ request }) => handler(request), - PUT: async ({ request }) => handler(request), - PATCH: async ({ request }) => handler(request), - DELETE: async ({ request }) => handler(request), - }, - }, + server: { handlers: toTanStackHandlers(handler) }, }) diff --git a/packages/cli/src/templates/tanstack/pages-route.tsx.hbs b/packages/cli/src/templates/tanstack/pages-route.tsx.hbs index 85a0c6e34..fdd96757f 100644 --- a/packages/cli/src/templates/tanstack/pages-route.tsx.hbs +++ b/packages/cli/src/templates/tanstack/pages-route.tsx.hbs @@ -1,31 +1,8 @@ -import { createFileRoute, notFound } from "@tanstack/react-router" -import { normalizePath } from "@btst/stack/client" -import { getStackClient } from "{{alias}}lib/stack-client" +import { createFileRoute } from "@tanstack/react-router" +import { createTanStackPageOptions } from "@btst/stack/tanstack" import { getOrCreateQueryClient } from "{{alias}}lib/query-client" +import { getStackClient } from "{{alias}}lib/stack-client" -export const Route = createFileRoute("/pages/$")({ - ssr: true, - component: BtstPagesRoute, - loader: async ({ params }) => { - const queryClient = getOrCreateQueryClient() - const routePath = normalizePath(params._splat) - const route = getStackClient(queryClient).router.getRoute(routePath) - if (!route) throw notFound() - if (route.loader) await route.loader() - return { meta: route.meta?.() } - }, - head: ({ loaderData }) => { - if (!loaderData?.meta || !Array.isArray(loaderData.meta)) { - return { title: "No Meta", meta: [{ title: "No Meta" }] } - } - return { meta: loaderData.meta } - }, -}) - -function BtstPagesRoute() { - const params = Route.useParams() - const queryClient = getOrCreateQueryClient() - const routePath = normalizePath(params._splat) - const route = getStackClient(queryClient).router.getRoute(routePath) - return route?.PageComponent ? :
Route not found
-} +export const Route = createFileRoute("/pages/$")( + createTanStackPageOptions({ getStackClient, getQueryClient: getOrCreateQueryClient }), +) diff --git a/packages/cli/src/utils/__tests__/scaffold-plan.test.ts b/packages/cli/src/utils/__tests__/scaffold-plan.test.ts index 43d7693c3..2694a8767 100644 --- a/packages/cli/src/utils/__tests__/scaffold-plan.test.ts +++ b/packages/cli/src/utils/__tests__/scaffold-plan.test.ts @@ -315,7 +315,7 @@ describe("scaffold plan", () => { 'import { getOrCreateQueryClient } from "@/lib/query-client"', ); expect(pagesRouteFile?.content).toContain( - "const queryClient = getOrCreateQueryClient()", + "getQueryClient: getOrCreateQueryClient", ); expect(pagesRouteFile?.content).not.toContain("new QueryClient()"); }); @@ -337,7 +337,7 @@ describe("scaffold plan", () => { 'import { getOrCreateQueryClient } from "@/lib/query-client"', ); expect(pagesRouteFile?.content).toContain( - "const queryClient = getOrCreateQueryClient()", + "getQueryClient: getOrCreateQueryClient", ); expect(pagesRouteFile?.content).not.toContain("context.queryClient"); }); @@ -740,7 +740,7 @@ describe("scaffold plan", () => { (f) => f.path === "app/pages/[[...all]]/page.tsx", ); expect(pagesRoute?.content).toContain("generateMetadata"); - expect(pagesRoute?.content).toContain("metaElementsToObject"); + expect(pagesRoute?.content).toContain("createNextPage"); }); it("emits SSG pages for nextjs when blog selected", async () => { diff --git a/packages/stack/src/__tests__/entry-factories.test.tsx b/packages/stack/src/__tests__/entry-factories.test.tsx new file mode 100644 index 000000000..24a29ee76 --- /dev/null +++ b/packages/stack/src/__tests__/entry-factories.test.tsx @@ -0,0 +1,258 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { renderToString } from "react-dom/server"; +import { describe, expect, it, vi } from "vitest"; +import { createNextPage, toNextRouteHandlers } from "../next"; +import { createReactRouterPage, toReactRouterHandlers } from "../react-router"; +import type { + StackClientLike, + StackRouteLike, +} from "../shared/entry-factories"; +import { createTanStackPageOptions, toTanStackHandlers } from "../tanstack"; + +function makeStackClient(routes: Record) { + return (_queryClient: QueryClient): StackClientLike => ({ + router: { + getRoute: (path: string) => routes[path] ?? null, + }, + }); +} + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); +} + +const okHandler = async (request: Request) => + new Response(`ok:${request.method}`); + +describe("API route handler factories", () => { + it("toNextRouteHandlers exposes the handler for all five methods", async () => { + const handlers = toNextRouteHandlers(okHandler); + expect(Object.keys(handlers)).toEqual([ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE", + ]); + const res = await handlers.POST( + new Request("http://test.local/api/data", { method: "POST" }), + ); + expect(await res.text()).toBe("ok:POST"); + }); + + it("toReactRouterHandlers delegates loader and action to the handler", async () => { + const { loader, action } = toReactRouterHandlers(okHandler); + const getRes = await loader({ + request: new Request("http://test.local/api/data"), + }); + expect(await getRes.text()).toBe("ok:GET"); + const postRes = await action({ + request: new Request("http://test.local/api/data", { method: "POST" }), + }); + expect(await postRes.text()).toBe("ok:POST"); + }); + + it("toTanStackHandlers delegates all five methods to the handler", async () => { + const handlers = toTanStackHandlers(okHandler); + expect(Object.keys(handlers)).toEqual([ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE", + ]); + const res = await handlers.DELETE({ + request: new Request("http://test.local/api/data", { method: "DELETE" }), + }); + expect(await res.text()).toBe("ok:DELETE"); + }); +}); + +describe("createNextPage", () => { + const params = (all?: string[]) => Promise.resolve({ all }); + + it("runs the loader and renders the page inside a HydrationBoundary", async () => { + const calls: string[] = []; + const queryClient = makeQueryClient(); + const page = createNextPage({ + getStackClient: makeStackClient({ + "/blog": { + PageComponent: () =>
blog page
, + loader: async () => { + calls.push("loader"); + }, + }, + }), + getQueryClient: () => queryClient, + }); + + const element = await page.Page({ params: params(["blog"]) }); + const html = renderToString( + {element}, + ); + expect(calls).toEqual(["loader"]); + expect(html).toContain("blog page"); + }); + + it("calls notFound when no route matches", async () => { + const notFound = vi.fn(() => { + throw new Error("NEXT_NOT_FOUND_TEST"); + }); + const page = createNextPage({ + getStackClient: makeStackClient({}), + getQueryClient: makeQueryClient, + notFound: notFound as unknown as () => never, + }); + + await expect(page.Page({ params: params(["missing"]) })).rejects.toThrow( + "NEXT_NOT_FOUND_TEST", + ); + expect(notFound).toHaveBeenCalledOnce(); + }); + + it("generateMetadata runs the loader before meta and converts elements", async () => { + const calls: string[] = []; + const page = createNextPage({ + getStackClient: makeStackClient({ + "/blog": { + loader: async () => { + calls.push("loader"); + }, + meta: () => { + calls.push("meta"); + return [ + { name: "title", content: "Blog Title" }, + { name: "description", content: "Blog Description" }, + ]; + }, + }, + }), + getQueryClient: makeQueryClient, + }); + + const metadata = await page.generateMetadata({ params: params(["blog"]) }); + expect(calls).toEqual(["loader", "meta"]); + expect(metadata).toMatchObject({ + title: "Blog Title", + description: "Blog Description", + }); + }); + + it("generateMetadata returns {} without running the loader when the route has no meta", async () => { + const loader = vi.fn(); + const page = createNextPage({ + getStackClient: makeStackClient({ "/blog": { loader } }), + getQueryClient: makeQueryClient, + }); + + const metadata = await page.generateMetadata({ params: params(["blog"]) }); + expect(metadata).toEqual({}); + expect(loader).not.toHaveBeenCalled(); + }); +}); + +describe("createReactRouterPage", () => { + it("loader prefetches and returns path, dehydratedState and meta", async () => { + const queryClient = makeQueryClient(); + const page = createReactRouterPage({ + getStackClient: makeStackClient({ + "/blog": { + loader: async () => { + await queryClient.prefetchQuery({ + queryKey: ["post"], + queryFn: async () => "post data", + }); + }, + meta: () => [{ name: "title", content: "Blog" }], + }, + }), + getQueryClient: () => queryClient, + }); + + const data = await page.loader({ params: { "*": "blog" } }); + expect(data.path).toBe("/blog"); + expect(data.meta).toEqual([{ name: "title", content: "Blog" }]); + expect(data.dehydratedState.queries).toHaveLength(1); + + // meta() maps loader data through (supports both arg field names) + expect(page.meta({ loaderData: data })).toEqual(data.meta); + expect(page.meta({ data })).toEqual(data.meta); + }); + + it("dehydrates failed queries so the client does not refetch on error", async () => { + const queryClient = makeQueryClient(); + const page = createReactRouterPage({ + getStackClient: makeStackClient({ + "/broken": { + loader: async () => { + await queryClient.prefetchQuery({ + queryKey: ["broken"], + queryFn: async () => { + throw new Error("boom"); + }, + }); + }, + }, + }), + getQueryClient: () => queryClient, + }); + + const data = await page.loader({ params: { "*": "broken" } }); + const errored = data.dehydratedState.queries.find( + (q) => q.state.status === "error", + ); + expect(errored).toBeDefined(); + }); +}); + +describe("createTanStackPageOptions", () => { + it("loader throws notFound() when no route matches", async () => { + const options = createTanStackPageOptions({ + getStackClient: makeStackClient({}), + getQueryClient: makeQueryClient, + }); + + await expect( + options.loader({ params: { _splat: "missing" } }), + ).rejects.toMatchObject({ isNotFound: true }); + }); + + it("loader uses the router context queryClient, runs route.loader and returns meta", async () => { + const contextQueryClient = makeQueryClient(); + const seen: QueryClient[] = []; + const options = createTanStackPageOptions({ + getStackClient: (queryClient) => { + seen.push(queryClient); + return makeStackClient({ + "/blog": { + loader: async () => {}, + meta: () => [{ title: "Blog" }], + }, + })(queryClient); + }, + }); + + const data = await options.loader({ + params: { _splat: "blog" }, + context: { queryClient: contextQueryClient }, + }); + expect(seen).toEqual([contextQueryClient]); + expect(data).toEqual({ meta: [{ title: "Blog" }] }); + }); + + it("head falls back when there is no meta and passes meta through otherwise", () => { + const options = createTanStackPageOptions({ + getStackClient: makeStackClient({}), + getQueryClient: makeQueryClient, + }); + + expect(options.head({ loaderData: undefined })).toEqual({ + title: "No Meta", + meta: [{ title: "No Meta" }], + }); + const meta = [{ title: "Blog" }]; + expect(options.head({ loaderData: { meta } })).toEqual({ meta }); + }); +}); diff --git a/packages/stack/src/next/handlers.ts b/packages/stack/src/next/handlers.ts new file mode 100644 index 000000000..67cdb6764 --- /dev/null +++ b/packages/stack/src/next/handlers.ts @@ -0,0 +1,24 @@ +import type { StackRequestHandler } from "../shared/entry-factories"; + +/** + * Wires a BTST API handler to the five HTTP method exports a Next.js route + * handler file needs. + * + * @example + * ```ts + * // app/api/data/[[...all]]/route.ts + * import { toNextRouteHandlers } from "@btst/stack/next"; + * import { handler } from "@/lib/stack"; + * + * export const { GET, POST, PUT, PATCH, DELETE } = toNextRouteHandlers(handler); + * ``` + */ +export function toNextRouteHandlers(handler: StackRequestHandler) { + return { + GET: handler, + POST: handler, + PUT: handler, + PATCH: handler, + DELETE: handler, + }; +} diff --git a/packages/stack/src/next/index.tsx b/packages/stack/src/next/index.tsx index 2d4af4c3d..a01b07f97 100644 --- a/packages/stack/src/next/index.tsx +++ b/packages/stack/src/next/index.tsx @@ -1,102 +1,11 @@ -"use client"; -import NextImage from "next/image"; -import NextLink from "next/link"; -import { useRouter } from "next/navigation"; -import { useMemo } from "react"; -import type { StackRouter, StackRouterConfig } from "../context/router"; - -function NextLinkWrapper({ - href, - ...props -}: React.ComponentProps<"a"> & Record) { - return ; -} - -/** - * Next.js Image wrapper for plugins. - * Handles both cases: with explicit dimensions or using fill mode. - */ -function NextImageWrapper(props: React.ImgHTMLAttributes) { - const { alt = "", src = "", width, height, ...rest } = props; - - // Use fill mode if width or height are not provided - if (!width || !height) { - return ( - - - - ); - } - - return ( - - ); -} - -// Reads window.location.search instead of Next's useSearchParams() hook to -// avoid forcing a Suspense/CSR bailout during static generation. Returns -// empty params on the server. -function getSearchParams(): URLSearchParams { - return new URLSearchParams( - typeof window !== "undefined" ? window.location.search : "", - ); -} - -function useNextStackRouter(): StackRouter { - const router = useRouter(); - - return useMemo( - () => ({ - navigate: (path: string) => { - router.push(path); - }, - refresh: () => { - router.refresh(); - }, - setSearchParams: ( - next: URLSearchParams, - opts?: { replace?: boolean }, - ) => { - const query = next.toString(); - const path = `${window.location.pathname}${query ? `?${query}` : ""}`; - if (opts?.replace) { - router.replace(path); - } else { - router.push(path); - } - }, - }), - [router], - ); -} - -/** - * Router preset for Next.js (App Router). - * - * @example - * ```tsx - * import { nextRouter } from "@btst/stack/next"; - * - * - * ``` - */ -export function nextRouter(): StackRouterConfig { - return { - Link: NextLinkWrapper, - Image: NextImageWrapper, - getSearchParams, - useRouter: useNextStackRouter, - }; -} +export { toNextRouteHandlers } from "./handlers"; +export { + type CreateNextPageOptions, + type NextPageProps, + createNextPage, +} from "./page"; +export { nextRouter } from "./router"; +export type { + GetStackClient, + StackRequestHandler, +} from "../shared/entry-factories"; diff --git a/packages/stack/src/next/page.tsx b/packages/stack/src/next/page.tsx new file mode 100644 index 000000000..ea458e146 --- /dev/null +++ b/packages/stack/src/next/page.tsx @@ -0,0 +1,95 @@ +import { HydrationBoundary, dehydrate } from "@tanstack/react-query"; +import type { QueryClient } from "@tanstack/react-query"; +import type { Metadata } from "next"; +import { notFound as nextNotFound } from "next/navigation"; +import type { ReactNode } from "react"; +import { metaElementsToObject } from "../client/meta-utils"; +import { normalizePath } from "../client/path-utils"; +import { + type GetStackClient, + stackDehydrateOptions, +} from "../shared/entry-factories"; + +export interface NextPageProps { + params: Promise<{ all?: string[] }>; +} + +export interface CreateNextPageOptions { + /** Returns the stack client for a given QueryClient (`lib/stack-client`). */ + getStackClient: GetStackClient; + /** Returns the QueryClient for the current context (`lib/query-client`). */ + getQueryClient: () => QueryClient; + /** + * Called when no route matches the path. Defaults to `notFound()` from + * `next/navigation`. + */ + notFound?: () => never; + /** Wraps the rendered page (inside the `HydrationBoundary`). */ + wrapPage?: (page: ReactNode) => ReactNode; +} + +/** + * Creates the Next.js catch-all page for BTST plugin routes: SSR prefetch via + * `route.loader()`, React Query dehydration (including failed queries), + * `generateMetadata` with loader-before-meta ordering, and 404 via + * `notFound()`. + * + * @example + * ```tsx + * // app/pages/[[...all]]/page.tsx + * import { createNextPage } from "@btst/stack/next"; + * import { getOrCreateQueryClient } from "@/lib/query-client"; + * import { getStackClient } from "@/lib/stack-client"; + * + * export const dynamic = "force-dynamic"; + * const page = createNextPage({ getStackClient, getQueryClient: getOrCreateQueryClient }); + * export default page.Page; + * export const generateMetadata = page.generateMetadata; + * ``` + */ +export function createNextPage(options: CreateNextPageOptions) { + const { + getStackClient, + getQueryClient, + notFound = nextNotFound, + wrapPage, + } = options; + + async function Page({ params }: NextPageProps) { + const pathParams = await params; + const path = normalizePath(pathParams?.all); + const queryClient = getQueryClient(); + const route = getStackClient(queryClient).router.getRoute(path); + + if (route?.loader) { + await route.loader(); + } + + const page = route?.PageComponent ? : notFound(); + + return ( + + {wrapPage ? wrapPage(page) : page} + + ); + } + + async function generateMetadata({ + params, + }: NextPageProps): Promise { + const pathParams = await params; + const path = normalizePath(pathParams?.all); + const queryClient = getQueryClient(); + const route = getStackClient(queryClient).router.getRoute(path); + + if (!route?.meta) { + return {}; + } + if (route.loader) { + await route.loader(); + } + return metaElementsToObject(await route.meta()) satisfies Metadata; + } + + return { Page, generateMetadata }; +} diff --git a/packages/stack/src/next/router.tsx b/packages/stack/src/next/router.tsx new file mode 100644 index 000000000..2d4af4c3d --- /dev/null +++ b/packages/stack/src/next/router.tsx @@ -0,0 +1,102 @@ +"use client"; +import NextImage from "next/image"; +import NextLink from "next/link"; +import { useRouter } from "next/navigation"; +import { useMemo } from "react"; +import type { StackRouter, StackRouterConfig } from "../context/router"; + +function NextLinkWrapper({ + href, + ...props +}: React.ComponentProps<"a"> & Record) { + return ; +} + +/** + * Next.js Image wrapper for plugins. + * Handles both cases: with explicit dimensions or using fill mode. + */ +function NextImageWrapper(props: React.ImgHTMLAttributes) { + const { alt = "", src = "", width, height, ...rest } = props; + + // Use fill mode if width or height are not provided + if (!width || !height) { + return ( + + + + ); + } + + return ( + + ); +} + +// Reads window.location.search instead of Next's useSearchParams() hook to +// avoid forcing a Suspense/CSR bailout during static generation. Returns +// empty params on the server. +function getSearchParams(): URLSearchParams { + return new URLSearchParams( + typeof window !== "undefined" ? window.location.search : "", + ); +} + +function useNextStackRouter(): StackRouter { + const router = useRouter(); + + return useMemo( + () => ({ + navigate: (path: string) => { + router.push(path); + }, + refresh: () => { + router.refresh(); + }, + setSearchParams: ( + next: URLSearchParams, + opts?: { replace?: boolean }, + ) => { + const query = next.toString(); + const path = `${window.location.pathname}${query ? `?${query}` : ""}`; + if (opts?.replace) { + router.replace(path); + } else { + router.push(path); + } + }, + }), + [router], + ); +} + +/** + * Router preset for Next.js (App Router). + * + * @example + * ```tsx + * import { nextRouter } from "@btst/stack/next"; + * + * + * ``` + */ +export function nextRouter(): StackRouterConfig { + return { + Link: NextLinkWrapper, + Image: NextImageWrapper, + getSearchParams, + useRouter: useNextStackRouter, + }; +} diff --git a/packages/stack/src/react-router/handlers.ts b/packages/stack/src/react-router/handlers.ts new file mode 100644 index 000000000..6408f75b6 --- /dev/null +++ b/packages/stack/src/react-router/handlers.ts @@ -0,0 +1,21 @@ +import type { StackRequestHandler } from "../shared/entry-factories"; + +/** + * Wires a BTST API handler to the `loader`/`action` exports a React Router + * catch-all API route needs. + * + * @example + * ```ts + * // app/routes/api/data/$.ts + * import { toReactRouterHandlers } from "@btst/stack/react-router"; + * import { handler } from "~/lib/stack"; + * + * export const { loader, action } = toReactRouterHandlers(handler); + * ``` + */ +export function toReactRouterHandlers(handler: StackRequestHandler) { + return { + loader: ({ request }: { request: Request }) => handler(request), + action: ({ request }: { request: Request }) => handler(request), + }; +} diff --git a/packages/stack/src/react-router/index.tsx b/packages/stack/src/react-router/index.tsx index 27b0b40f1..4b9783e53 100644 --- a/packages/stack/src/react-router/index.tsx +++ b/packages/stack/src/react-router/index.tsx @@ -1,63 +1,11 @@ -"use client"; -import { useMemo } from "react"; -import { - Link as ReactRouterLink, - useNavigate, - useRevalidator, - useSearchParams, -} from "react-router"; -import type { StackRouter, StackRouterConfig } from "../context/router"; - -function ReactRouterLinkWrapper({ - href, - children, - ...props -}: React.ComponentProps<"a"> & Record) { - return ( - - {children} - - ); -} - -function useReactRouterStackRouter(): StackRouter { - const navigate = useNavigate(); - const { revalidate } = useRevalidator(); - const [searchParams, setSearchParams] = useSearchParams(); - - return useMemo( - () => ({ - navigate: (path: string) => { - void navigate(path); - }, - refresh: () => { - void revalidate(); - }, - getSearchParams: () => new URLSearchParams(searchParams), - setSearchParams: ( - next: URLSearchParams, - opts?: { replace?: boolean }, - ) => { - setSearchParams(next, { replace: opts?.replace }); - }, - }), - [navigate, revalidate, searchParams, setSearchParams], - ); -} - -/** - * Router preset for React Router (v7). - * - * @example - * ```tsx - * import { reactRouter } from "@btst/stack/react-router"; - * - * - * ``` - */ -export function reactRouter(): StackRouterConfig { - return { - Link: ReactRouterLinkWrapper, - useRouter: useReactRouterStackRouter, - }; -} +export { toReactRouterHandlers } from "./handlers"; +export { + type CreateReactRouterPageOptions, + type ReactRouterPageLoaderArgs, + createReactRouterPage, +} from "./page"; +export { reactRouter } from "./router"; +export type { + GetStackClient, + StackRequestHandler, +} from "../shared/entry-factories"; diff --git a/packages/stack/src/react-router/page.tsx b/packages/stack/src/react-router/page.tsx new file mode 100644 index 000000000..b4e8f0be9 --- /dev/null +++ b/packages/stack/src/react-router/page.tsx @@ -0,0 +1,105 @@ +import { + HydrationBoundary, + dehydrate, + useQueryClient, +} from "@tanstack/react-query"; +import type { QueryClient } from "@tanstack/react-query"; +import type { ComponentType, ReactNode } from "react"; +import { useLoaderData, useRouteError } from "react-router"; +import { normalizePath } from "../client/path-utils"; +import { + type GetStackClient, + stackDehydrateOptions, +} from "../shared/entry-factories"; + +/** + * Loose structural form of React Router's generated `Route.LoaderArgs` / + * `Route.MetaArgs` for a catch-all route. The generated `./+types/$` types + * are not available inside the library, so the factory types the subset it + * uses; the results remain assignable to the generated route exports. + */ +export interface ReactRouterPageLoaderArgs { + params: { "*"?: string } & Record; +} + +export interface CreateReactRouterPageOptions { + /** Returns the stack client for a given QueryClient (`lib/stack-client`). */ + getStackClient: GetStackClient; + /** Returns the QueryClient for the current context (`lib/query-client`). */ + getQueryClient: () => QueryClient; + /** Rendered when no route matches. Defaults to a "Route not found" div. */ + NotFound?: ComponentType; + /** Wraps the rendered page (inside the `HydrationBoundary`). */ + wrapPage?: (page: ReactNode) => ReactNode; +} + +/** + * Creates the React Router catch-all route pieces for BTST plugin routes: + * SSR prefetch via `route.loader()`, React Query dehydration (including + * failed queries), and loader-before-meta ordering. + * + * @example + * ```tsx + * // app/routes/pages/$.tsx + * import { createReactRouterPage } from "@btst/stack/react-router"; + * import { getOrCreateQueryClient } from "~/lib/query-client"; + * import { getStackClient } from "~/lib/stack-client"; + * + * const page = createReactRouterPage({ getStackClient, getQueryClient: getOrCreateQueryClient }); + * export const loader = page.loader; + * export const meta = page.meta; + * export default page.Component; + * ``` + */ +export function createReactRouterPage(options: CreateReactRouterPageOptions) { + const { getStackClient, getQueryClient, NotFound, wrapPage } = options; + + async function loader({ params }: ReactRouterPageLoaderArgs) { + const queryClient = getQueryClient(); + const path = normalizePath(params["*"]); + const route = getStackClient(queryClient).router.getRoute(path); + + if (route?.loader) { + await route.loader(); + } + + return { + path, + dehydratedState: dehydrate(queryClient, stackDehydrateOptions), + meta: await route?.meta?.(), + }; + } + + type LoaderData = Awaited>; + + // Recent React Router versions pass `loaderData`; older 7.x used `data`. + function meta(args: { loaderData?: LoaderData; data?: LoaderData }) { + return (args.loaderData ?? args.data)?.meta; + } + + function Component() { + const data = useLoaderData(); + const queryClient = useQueryClient(); + const route = getStackClient(queryClient).router.getRoute(data.path); + const page = route?.PageComponent ? ( + + ) : NotFound ? ( + + ) : ( +
Route not found
+ ); + + return ( + + {wrapPage ? wrapPage(page) : page} + + ); + } + + function ErrorBoundary() { + const error = useRouteError(); + return
{String(error)}
; + } + + return { loader, meta, Component, ErrorBoundary }; +} diff --git a/packages/stack/src/react-router/router.tsx b/packages/stack/src/react-router/router.tsx new file mode 100644 index 000000000..27b0b40f1 --- /dev/null +++ b/packages/stack/src/react-router/router.tsx @@ -0,0 +1,63 @@ +"use client"; +import { useMemo } from "react"; +import { + Link as ReactRouterLink, + useNavigate, + useRevalidator, + useSearchParams, +} from "react-router"; +import type { StackRouter, StackRouterConfig } from "../context/router"; + +function ReactRouterLinkWrapper({ + href, + children, + ...props +}: React.ComponentProps<"a"> & Record) { + return ( + + {children} + + ); +} + +function useReactRouterStackRouter(): StackRouter { + const navigate = useNavigate(); + const { revalidate } = useRevalidator(); + const [searchParams, setSearchParams] = useSearchParams(); + + return useMemo( + () => ({ + navigate: (path: string) => { + void navigate(path); + }, + refresh: () => { + void revalidate(); + }, + getSearchParams: () => new URLSearchParams(searchParams), + setSearchParams: ( + next: URLSearchParams, + opts?: { replace?: boolean }, + ) => { + setSearchParams(next, { replace: opts?.replace }); + }, + }), + [navigate, revalidate, searchParams, setSearchParams], + ); +} + +/** + * Router preset for React Router (v7). + * + * @example + * ```tsx + * import { reactRouter } from "@btst/stack/react-router"; + * + * + * ``` + */ +export function reactRouter(): StackRouterConfig { + return { + Link: ReactRouterLinkWrapper, + useRouter: useReactRouterStackRouter, + }; +} diff --git a/packages/stack/src/shared/entry-factories.ts b/packages/stack/src/shared/entry-factories.ts new file mode 100644 index 000000000..837c4c657 --- /dev/null +++ b/packages/stack/src/shared/entry-factories.ts @@ -0,0 +1,55 @@ +import { + type DehydrateOptions, + type QueryClient, + defaultShouldDehydrateQuery, +} from "@tanstack/react-query"; +import type { ComponentType } from "react"; + +/** + * Minimal structural view of a route returned by + * `createStackClient(...).router.getRoute(path)`. The framework entry + * factories only need these three fields. + */ +export interface StackRouteLike { + PageComponent?: ComponentType | undefined; + loader?: ((...args: any[]) => unknown) | undefined; + meta?: ((...args: any[]) => any) | undefined; +} + +/** + * Minimal structural view of the object returned by `createStackClient`. + * Any concrete `ClientLib` is assignable to this shape. + */ +export interface StackClientLike { + router: { + getRoute: ( + path: string, + queryParams?: Record, + ) => (StackRouteLike & Record) | null | undefined; + }; +} + +/** + * Consumer-provided factory that returns the stack client for a given + * QueryClient (per-request on the server, singleton on the client). + */ +export type GetStackClient = (queryClient: QueryClient) => StackClientLike; + +/** + * A framework-agnostic BTST API handler, as returned by + * `createBackendHandler(...).handler`. + */ +export type StackRequestHandler = ( + request: Request, +) => Response | Promise; + +/** + * Dehydration config owned by the page factories: dehydrate everything the + * default would, plus failed queries, so the client does not refetch (and + * flash a loading state) for queries that errored during SSR — regardless of + * how the consumer configured their QueryClient. + */ +export const stackDehydrateOptions: DehydrateOptions = { + shouldDehydrateQuery: (query) => + defaultShouldDehydrateQuery(query) || query.state.status === "error", +}; diff --git a/packages/stack/src/tanstack/handlers.ts b/packages/stack/src/tanstack/handlers.ts new file mode 100644 index 000000000..5445c7af4 --- /dev/null +++ b/packages/stack/src/tanstack/handlers.ts @@ -0,0 +1,28 @@ +import type { StackRequestHandler } from "../shared/entry-factories"; + +/** + * Wires a BTST API handler to the method handler map a TanStack Start server + * route needs. + * + * @example + * ```ts + * // src/routes/api/data/$.ts + * import { createFileRoute } from "@tanstack/react-router"; + * import { toTanStackHandlers } from "@btst/stack/tanstack"; + * import { handler } from "@/lib/stack"; + * + * export const Route = createFileRoute("/api/data/$")({ + * server: { handlers: toTanStackHandlers(handler) }, + * }); + * ``` + */ +export function toTanStackHandlers(handler: StackRequestHandler) { + const methodHandler = ({ request }: { request: Request }) => handler(request); + return { + GET: methodHandler, + POST: methodHandler, + PUT: methodHandler, + PATCH: methodHandler, + DELETE: methodHandler, + }; +} diff --git a/packages/stack/src/tanstack/index.tsx b/packages/stack/src/tanstack/index.tsx index 9451bf423..f91f00667 100644 --- a/packages/stack/src/tanstack/index.tsx +++ b/packages/stack/src/tanstack/index.tsx @@ -1,61 +1,10 @@ -"use client"; -import { Link as TanStackLink, useRouter } from "@tanstack/react-router"; -import { useMemo } from "react"; -import type { StackRouter, StackRouterConfig } from "../context/router"; - -function TanStackLinkWrapper({ - href, - children, - ...props -}: React.ComponentProps<"a"> & Record) { - return ( - - {children} - - ); -} - -function useTanStackStackRouter(): StackRouter { - const router = useRouter(); - - return useMemo( - () => ({ - navigate: (path: string) => { - router.navigate({ href: path }); - }, - refresh: () => { - router.invalidate(); - }, - getSearchParams: () => - new URLSearchParams(router.state.location.searchStr ?? ""), - setSearchParams: ( - next: URLSearchParams, - opts?: { replace?: boolean }, - ) => { - const query = next.toString(); - router.navigate({ - href: `${router.state.location.pathname}${query ? `?${query}` : ""}`, - replace: opts?.replace, - }); - }, - }), - [router], - ); -} - -/** - * Router preset for TanStack Router / TanStack Start. - * - * @example - * ```tsx - * import { tanstackRouter } from "@btst/stack/tanstack"; - * - * - * ``` - */ -export function tanstackRouter(): StackRouterConfig { - return { - Link: TanStackLinkWrapper, - useRouter: useTanStackStackRouter, - }; -} +export { toTanStackHandlers } from "./handlers"; +export { + type CreateTanStackPageOptions, + createTanStackPageOptions, +} from "./page"; +export { tanstackRouter } from "./router"; +export type { + GetStackClient, + StackRequestHandler, +} from "../shared/entry-factories"; diff --git a/packages/stack/src/tanstack/page.tsx b/packages/stack/src/tanstack/page.tsx new file mode 100644 index 000000000..0cd9e1641 --- /dev/null +++ b/packages/stack/src/tanstack/page.tsx @@ -0,0 +1,88 @@ +import type { QueryClient } from "@tanstack/react-query"; +import { notFound, useParams, useRouteContext } from "@tanstack/react-router"; +import { normalizePath } from "../client/path-utils"; +import type { GetStackClient } from "../shared/entry-factories"; + +export interface CreateTanStackPageOptions { + /** Returns the stack client for a given QueryClient (`lib/stack-client`). */ + getStackClient: GetStackClient; + /** + * Returns the QueryClient for the current context. Defaults to the + * `queryClient` from the router context (set up by + * `setupRouterSsrQueryIntegration`). + */ + getQueryClient?: () => QueryClient; +} + +interface TanStackPageLoaderArgs { + params: { _splat?: string }; + context?: unknown; +} + +/** + * Creates the route options for the TanStack Start catch-all page, to spread + * into `createFileRoute("/pages/$")(...)`: SSR prefetch via `route.loader()`, + * head/meta from `route.meta()` with loader-before-meta ordering, and 404 via + * `notFound()`. Query cache dehydration is handled by TanStack's router-query + * SSR integration. + * + * @example + * ```tsx + * // src/routes/pages/$.tsx + * import { createFileRoute } from "@tanstack/react-router"; + * import { createTanStackPageOptions } from "@btst/stack/tanstack"; + * import { getStackClient } from "@/lib/stack-client"; + * + * export const Route = createFileRoute("/pages/$")( + * createTanStackPageOptions({ getStackClient }), + * ); + * ``` + */ +export function createTanStackPageOptions(options: CreateTanStackPageOptions) { + const { getStackClient, getQueryClient } = options; + + function resolveQueryClient(context: unknown): QueryClient { + const fromContext = (context as { queryClient?: QueryClient } | null) + ?.queryClient; + const queryClient = getQueryClient?.() ?? fromContext; + if (!queryClient) { + throw new Error( + "createTanStackPageOptions: no QueryClient available. Provide `getQueryClient` or add `queryClient` to the router context.", + ); + } + return queryClient; + } + + function PageComponent() { + const params = useParams({ strict: false }) as { _splat?: string }; + const context = useRouteContext({ strict: false }); + const routePath = normalizePath(params._splat); + const route = getStackClient(resolveQueryClient(context)).router.getRoute( + routePath, + ); + return route?.PageComponent ? ( + + ) : ( +
Route not found
+ ); + } + + return { + ssr: true, + component: PageComponent, + loader: async ({ params, context }: TanStackPageLoaderArgs) => { + const queryClient = resolveQueryClient(context); + const routePath = normalizePath(params._splat); + const route = getStackClient(queryClient).router.getRoute(routePath); + if (!route) throw notFound(); + if (route.loader) await route.loader(); + return { meta: await route.meta?.() }; + }, + head: ({ loaderData }: { loaderData?: { meta?: unknown } }) => { + if (!loaderData?.meta || !Array.isArray(loaderData.meta)) { + return { title: "No Meta", meta: [{ title: "No Meta" }] }; + } + return { meta: loaderData.meta }; + }, + } as const; +} diff --git a/packages/stack/src/tanstack/router.tsx b/packages/stack/src/tanstack/router.tsx new file mode 100644 index 000000000..9451bf423 --- /dev/null +++ b/packages/stack/src/tanstack/router.tsx @@ -0,0 +1,61 @@ +"use client"; +import { Link as TanStackLink, useRouter } from "@tanstack/react-router"; +import { useMemo } from "react"; +import type { StackRouter, StackRouterConfig } from "../context/router"; + +function TanStackLinkWrapper({ + href, + children, + ...props +}: React.ComponentProps<"a"> & Record) { + return ( + + {children} + + ); +} + +function useTanStackStackRouter(): StackRouter { + const router = useRouter(); + + return useMemo( + () => ({ + navigate: (path: string) => { + router.navigate({ href: path }); + }, + refresh: () => { + router.invalidate(); + }, + getSearchParams: () => + new URLSearchParams(router.state.location.searchStr ?? ""), + setSearchParams: ( + next: URLSearchParams, + opts?: { replace?: boolean }, + ) => { + const query = next.toString(); + router.navigate({ + href: `${router.state.location.pathname}${query ? `?${query}` : ""}`, + replace: opts?.replace, + }); + }, + }), + [router], + ); +} + +/** + * Router preset for TanStack Router / TanStack Start. + * + * @example + * ```tsx + * import { tanstackRouter } from "@btst/stack/tanstack"; + * + * + * ``` + */ +export function tanstackRouter(): StackRouterConfig { + return { + Link: TanStackLinkWrapper, + useRouter: useTanStackStackRouter, + }; +} diff --git a/scripts/codegen/files/tanstack/src/routes/pages/$.tsx b/scripts/codegen/files/tanstack/src/routes/pages/$.tsx index 48cad0601..df282181f 100644 --- a/scripts/codegen/files/tanstack/src/routes/pages/$.tsx +++ b/scripts/codegen/files/tanstack/src/routes/pages/$.tsx @@ -1,34 +1,7 @@ -import { createFileRoute, notFound } from "@tanstack/react-router"; -import { normalizePath } from "@btst/stack/client"; +import { createTanStackPageOptions } from "@btst/stack/tanstack"; +import { createFileRoute } from "@tanstack/react-router"; import { getStackClient } from "@/lib/stack-client"; -export const Route = createFileRoute("/pages/$")({ - ssr: true, - component: BtstPagesRoute, - loader: async ({ params, context }) => { - const queryClient = context.queryClient; - const routePath = normalizePath(params._splat); - const route = getStackClient(queryClient).router.getRoute(routePath); - if (!route) throw notFound(); - if (route.loader) await route.loader(); - return { meta: route.meta?.() }; - }, - head: ({ loaderData }) => { - if (!loaderData?.meta || !Array.isArray(loaderData.meta)) { - return { title: "No Meta", meta: [{ title: "No Meta" }] }; - } - return { meta: loaderData.meta }; - }, -}); - -function BtstPagesRoute() { - const params = Route.useParams(); - const { queryClient } = Route.useRouteContext(); - const routePath = normalizePath(params._splat); - const route = getStackClient(queryClient).router.getRoute(routePath); - return route?.PageComponent ? ( - - ) : ( -
Route not found
- ); -} +export const Route = createFileRoute("/pages/$")( + createTanStackPageOptions({ getStackClient }), +); From e5b7c23f7414e204133d056f44022523bb4b874f Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sat, 4 Jul 2026 00:40:02 +0000 Subject: [PATCH 009/380] fix: avoid destructured route exports in react-router API route template React Router's build-time route-exports plugin cannot strip destructured exports from route modules ("Cannot remove destructured export \"loader\""), so assign loader/action individually from toReactRouterHandlers(). Co-authored-by: Cursor --- docs/content/docs/installation.mdx | 6 +++++- packages/cli/src/templates/react-router/api-route.ts.hbs | 6 +++++- packages/stack/src/react-router/handlers.ts | 7 ++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/content/docs/installation.mdx b/docs/content/docs/installation.mdx index 3ff2ad02d..422e71261 100644 --- a/docs/content/docs/installation.mdx +++ b/docs/content/docs/installation.mdx @@ -376,7 +376,11 @@ In order to use BTST, your application must meet the following requirements: import { toReactRouterHandlers } from "@btst/stack/react-router" import { handler } from "~/lib/stack" - export const { loader, action } = toReactRouterHandlers(handler) + // React Router's build can't strip destructured exports from route + // modules, so assign loader/action individually. + const handlers = toReactRouterHandlers(handler) + export const loader = handlers.loader + export const action = handlers.action ``` diff --git a/packages/cli/src/templates/react-router/api-route.ts.hbs b/packages/cli/src/templates/react-router/api-route.ts.hbs index ad62a8446..588bb6a30 100644 --- a/packages/cli/src/templates/react-router/api-route.ts.hbs +++ b/packages/cli/src/templates/react-router/api-route.ts.hbs @@ -1,4 +1,8 @@ import { toReactRouterHandlers } from "@btst/stack/react-router" import { handler } from "{{alias}}lib/stack" -export const { loader, action } = toReactRouterHandlers(handler) +// React Router's build can't strip destructured exports from route modules, +// so assign loader/action individually instead of destructuring the export. +const handlers = toReactRouterHandlers(handler) +export const loader = handlers.loader +export const action = handlers.action diff --git a/packages/stack/src/react-router/handlers.ts b/packages/stack/src/react-router/handlers.ts index 6408f75b6..ee3c88e09 100644 --- a/packages/stack/src/react-router/handlers.ts +++ b/packages/stack/src/react-router/handlers.ts @@ -4,13 +4,18 @@ import type { StackRequestHandler } from "../shared/entry-factories"; * Wires a BTST API handler to the `loader`/`action` exports a React Router * catch-all API route needs. * + * Note: React Router's build cannot strip destructured exports from route + * modules, so export the fields individually rather than destructuring. + * * @example * ```ts * // app/routes/api/data/$.ts * import { toReactRouterHandlers } from "@btst/stack/react-router"; * import { handler } from "~/lib/stack"; * - * export const { loader, action } = toReactRouterHandlers(handler); + * const handlers = toReactRouterHandlers(handler); + * export const loader = handlers.loader; + * export const action = handlers.action; * ``` */ export function toReactRouterHandlers(handler: StackRequestHandler) { From 94f5db7a6f25e438d3b9fdfa6c191830b04d37c1 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sat, 4 Jul 2026 01:04:24 +0000 Subject: [PATCH 010/380] fix: address review feedback on entry factories - generateMetadata now calls notFound() for unknown paths, matching Page - createReactRouterPage accepts an ErrorBoundary override so consumers can ship a production-safe error UI without abandoning the factory - both page factories accept dehydrateOptions to override the default failed-query dehydration (e.g. to sanitize error payloads) Co-authored-by: Cursor --- docs/content/docs/installation.mdx | 4 +- .../src/__tests__/entry-factories.test.tsx | 42 +++++++++++++++++++ packages/stack/src/next/page.tsx | 17 ++++++-- packages/stack/src/react-router/page.tsx | 35 +++++++++++++--- 4 files changed, 88 insertions(+), 10 deletions(-) diff --git a/docs/content/docs/installation.mdx b/docs/content/docs/installation.mdx index 422e71261..d0174d046 100644 --- a/docs/content/docs/installation.mdx +++ b/docs/content/docs/installation.mdx @@ -865,8 +865,8 @@ In order to use BTST, your application must meet the following requirements: The factory matches the URL to a plugin route via `stackClient.router.getRoute(path)`, prefetches data server-side with `route.loader()`, renders the route's `PageComponent` with instant hydration on the client, and generates SEO metadata from `route.meta()` (running the loader first, so meta can read prefetched data). **Escape hatches:** - - `createNextPage` accepts `notFound` (replaces `notFound()` from `next/navigation`) and `wrapPage` - - `createReactRouterPage` accepts `NotFound` (component rendered when no route matches) and `wrapPage` + - `createNextPage` accepts `notFound` (replaces `notFound()` from `next/navigation`), `wrapPage`, and `dehydrateOptions` + - `createReactRouterPage` accepts `NotFound` (component rendered when no route matches), `ErrorBoundary` (production-safe error UI), `wrapPage`, and `dehydrateOptions` - `createTanStackPageOptions` accepts `getQueryClient` (defaults to the router context's `queryClient`); spread extra route options like `notFoundComponent` alongside it - Hand-writing the route file remains fully supported — see below diff --git a/packages/stack/src/__tests__/entry-factories.test.tsx b/packages/stack/src/__tests__/entry-factories.test.tsx index 24a29ee76..21e1875b0 100644 --- a/packages/stack/src/__tests__/entry-factories.test.tsx +++ b/packages/stack/src/__tests__/entry-factories.test.tsx @@ -151,6 +151,22 @@ describe("createNextPage", () => { expect(metadata).toEqual({}); expect(loader).not.toHaveBeenCalled(); }); + + it("generateMetadata calls notFound when no route matches", async () => { + const notFound = vi.fn(() => { + throw new Error("NEXT_NOT_FOUND_TEST"); + }); + const page = createNextPage({ + getStackClient: makeStackClient({}), + getQueryClient: makeQueryClient, + notFound: notFound as unknown as () => never, + }); + + await expect( + page.generateMetadata({ params: params(["missing"]) }), + ).rejects.toThrow("NEXT_NOT_FOUND_TEST"); + expect(notFound).toHaveBeenCalledOnce(); + }); }); describe("createReactRouterPage", () => { @@ -205,6 +221,32 @@ describe("createReactRouterPage", () => { ); expect(errored).toBeDefined(); }); + + it("supports custom ErrorBoundary and dehydrateOptions overrides", async () => { + const queryClient = makeQueryClient(); + const CustomErrorBoundary = () =>
custom error
; + const page = createReactRouterPage({ + getStackClient: makeStackClient({ + "/broken": { + loader: async () => { + await queryClient.prefetchQuery({ + queryKey: ["broken"], + queryFn: async () => { + throw new Error("boom"); + }, + }); + }, + }, + }), + getQueryClient: () => queryClient, + ErrorBoundary: CustomErrorBoundary, + dehydrateOptions: { shouldDehydrateQuery: () => false }, + }); + + expect(page.ErrorBoundary).toBe(CustomErrorBoundary); + const data = await page.loader({ params: { "*": "broken" } }); + expect(data.dehydratedState.queries).toHaveLength(0); + }); }); describe("createTanStackPageOptions", () => { diff --git a/packages/stack/src/next/page.tsx b/packages/stack/src/next/page.tsx index ea458e146..3e59b3597 100644 --- a/packages/stack/src/next/page.tsx +++ b/packages/stack/src/next/page.tsx @@ -1,5 +1,5 @@ import { HydrationBoundary, dehydrate } from "@tanstack/react-query"; -import type { QueryClient } from "@tanstack/react-query"; +import type { DehydrateOptions, QueryClient } from "@tanstack/react-query"; import type { Metadata } from "next"; import { notFound as nextNotFound } from "next/navigation"; import type { ReactNode } from "react"; @@ -26,6 +26,13 @@ export interface CreateNextPageOptions { notFound?: () => never; /** Wraps the rendered page (inside the `HydrationBoundary`). */ wrapPage?: (page: ReactNode) => ReactNode; + /** + * Options passed to `dehydrate()`. Defaults to dehydrating failed queries + * in addition to the React Query defaults, so the client does not refetch + * queries that errored during SSR. Override e.g. to sanitize error + * payloads before they are serialized into the HTML. + */ + dehydrateOptions?: DehydrateOptions; } /** @@ -53,6 +60,7 @@ export function createNextPage(options: CreateNextPageOptions) { getQueryClient, notFound = nextNotFound, wrapPage, + dehydrateOptions = stackDehydrateOptions, } = options; async function Page({ params }: NextPageProps) { @@ -68,7 +76,7 @@ export function createNextPage(options: CreateNextPageOptions) { const page = route?.PageComponent ? : notFound(); return ( - + {wrapPage ? wrapPage(page) : page} ); @@ -82,7 +90,10 @@ export function createNextPage(options: CreateNextPageOptions) { const queryClient = getQueryClient(); const route = getStackClient(queryClient).router.getRoute(path); - if (!route?.meta) { + if (!route) { + return notFound(); + } + if (!route.meta) { return {}; } if (route.loader) { diff --git a/packages/stack/src/react-router/page.tsx b/packages/stack/src/react-router/page.tsx index b4e8f0be9..0f3845764 100644 --- a/packages/stack/src/react-router/page.tsx +++ b/packages/stack/src/react-router/page.tsx @@ -3,7 +3,7 @@ import { dehydrate, useQueryClient, } from "@tanstack/react-query"; -import type { QueryClient } from "@tanstack/react-query"; +import type { DehydrateOptions, QueryClient } from "@tanstack/react-query"; import type { ComponentType, ReactNode } from "react"; import { useLoaderData, useRouteError } from "react-router"; import { normalizePath } from "../client/path-utils"; @@ -29,8 +29,21 @@ export interface CreateReactRouterPageOptions { getQueryClient: () => QueryClient; /** Rendered when no route matches. Defaults to a "Route not found" div. */ NotFound?: ComponentType; + /** + * Rendered when the route errors. Defaults to a `
` with the
+	 * stringified error — provide your own component for a production-safe
+	 * error UI (use `useRouteError()` from react-router to read the error).
+	 */
+	ErrorBoundary?: ComponentType;
 	/** Wraps the rendered page (inside the `HydrationBoundary`). */
 	wrapPage?: (page: ReactNode) => ReactNode;
+	/**
+	 * Options passed to `dehydrate()`. Defaults to dehydrating failed queries
+	 * in addition to the React Query defaults, so the client does not refetch
+	 * queries that errored during SSR. Override e.g. to sanitize error
+	 * payloads before they are serialized into the HTML.
+	 */
+	dehydrateOptions?: DehydrateOptions;
 }
 
 /**
@@ -52,7 +65,14 @@ export interface CreateReactRouterPageOptions {
  * ```
  */
 export function createReactRouterPage(options: CreateReactRouterPageOptions) {
-	const { getStackClient, getQueryClient, NotFound, wrapPage } = options;
+	const {
+		getStackClient,
+		getQueryClient,
+		NotFound,
+		ErrorBoundary: CustomErrorBoundary,
+		wrapPage,
+		dehydrateOptions = stackDehydrateOptions,
+	} = options;
 
 	async function loader({ params }: ReactRouterPageLoaderArgs) {
 		const queryClient = getQueryClient();
@@ -65,7 +85,7 @@ export function createReactRouterPage(options: CreateReactRouterPageOptions) {
 
 		return {
 			path,
-			dehydratedState: dehydrate(queryClient, stackDehydrateOptions),
+			dehydratedState: dehydrate(queryClient, dehydrateOptions),
 			meta: await route?.meta?.(),
 		};
 	}
@@ -96,10 +116,15 @@ export function createReactRouterPage(options: CreateReactRouterPageOptions) {
 		);
 	}
 
-	function ErrorBoundary() {
+	function DefaultErrorBoundary() {
 		const error = useRouteError();
 		return 
{String(error)}
; } - return { loader, meta, Component, ErrorBoundary }; + return { + loader, + meta, + Component, + ErrorBoundary: CustomErrorBoundary ?? DefaultErrorBoundary, + }; } From 48fa7b2e046686b003c478beea113d4c9c26ba08 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:39:17 +0000 Subject: [PATCH 011/380] feat: auth provider contract for centralized identity and permissions Core-only phase of #128: optional auth providers on StackProvider (client) and stack() (server). Adds useIdentity()/useCan() hooks, the element-gating component, a permission prop on ComposedRoute with loginPath redirect through the top-level router, and per-request memoized identity resolution exposed via getRequestIdentity() for lifecycle hooks. Fully non-breaking: without a provider all code paths behave exactly as before. Co-authored-by: Cursor --- docs/content/docs/auth.mdx | 158 ++++++++ docs/content/docs/meta.json | 1 + packages/stack/package.json | 3 +- .../src/__tests__/auth-provider.test.tsx | 369 ++++++++++++++++++ .../stack/src/__tests__/stack-auth.test.ts | 129 ++++++ packages/stack/src/api/index.ts | 81 +++- .../stack/src/client/components/compose.tsx | 77 +++- packages/stack/src/context/auth.tsx | 221 +++++++++++ packages/stack/src/context/index.ts | 7 + packages/stack/src/context/provider.tsx | 33 +- packages/stack/src/shared/auth-types.ts | 98 +++++ packages/stack/src/types.ts | 17 + pnpm-lock.yaml | 277 ++++--------- 13 files changed, 1267 insertions(+), 204 deletions(-) create mode 100644 docs/content/docs/auth.mdx create mode 100644 packages/stack/src/__tests__/auth-provider.test.tsx create mode 100644 packages/stack/src/__tests__/stack-auth.test.ts create mode 100644 packages/stack/src/context/auth.tsx create mode 100644 packages/stack/src/shared/auth-types.ts diff --git a/docs/content/docs/auth.mdx b/docs/content/docs/auth.mdx new file mode 100644 index 000000000..8832032ea --- /dev/null +++ b/docs/content/docs/auth.mdx @@ -0,0 +1,158 @@ +--- +title: Auth Provider +description: Centralized identity and permissions across client and backend with one optional auth provider +--- + +BTST has an optional auth provider contract: define identity resolution and permission checks once, and every plugin consumes them through shared hooks and components. Without a provider configured, everything behaves exactly as before — identity is `null`, all permission checks pass, and `` renders its children. + +## Client provider + +Pass an auth provider to `StackProvider`: + +```tsx +import { StackProvider, type StackAuthProvider } from "@btst/stack/context"; + +const authProvider: StackAuthProvider = { + // Resolve the current user (null when unauthenticated) + getIdentity: async () => { + const session = await getSession(); + return session?.user ?? null; + }, + // Optional: permission check. Omit to allow everything. + can: ({ resource, action, identity }) => { + if (!identity) return false; + return checkPermission(identity, resource, action); // e.g. "blog:post", "delete" + }, + // Optional: where to send unauthenticated users on gated routes + loginPath: "/login", +}; + + + {children} +; +``` + + + +### better-auth example + +```tsx +import { createAuthClient } from "better-auth/react"; +import type { StackAuthProvider } from "@btst/stack/context"; + +const authClient = createAuthClient(); + +const authProvider: StackAuthProvider = { + getIdentity: async () => { + const { data } = await authClient.getSession(); + return data?.user ?? null; + }, + can: async ({ resource, action, identity }) => { + if (!identity) return false; + // Map resource/action onto your better-auth roles or permissions + return identity.role === "admin" || action === "read"; + }, + loginPath: "/login", +}; +``` + +## `useIdentity()` and `useCan()` + +```tsx +import { useCan, useIdentity } from "@btst/stack/context"; + +function Toolbar() { + const { identity, isPending } = useIdentity(); + const { can: canDelete } = useCan({ resource: "blog:post", action: "delete" }); + + if (isPending) return null; + return ( +
+ {identity ? `Signed in as ${identity.name}` : "Anonymous"} + {canDelete && } +
+ ); +} +``` + +- `useIdentity()` returns `{ identity, isPending, refetch }`. Call `refetch()` after login/logout. +- `useCan({ resource, action, params })` returns `{ can, isPending }`. Without a provider (or without a `can` function) it resolves to `{ can: true, isPending: false }` immediately. + +## `` — element-level gating + +Wrap permission-sensitive UI. Children render when `can()` allows, `fallback` (default `null`) otherwise: + +```tsx +import { CanAccess } from "@btst/stack/context"; + + + +; +``` + +While the check is pending, the optional `loading` node (default `null`) renders instead — gated UI never flashes. Without an auth provider configured, `` always renders its children. + +## Route gating + +`ComposedRoute` (the wrapper every plugin page uses) accepts an optional `permission`: + +```tsx + +``` + +When an auth provider is configured and `can()` denies access: + +- **Unauthenticated** users are redirected to the provider's `loginPath` (via the top-level `router`'s `navigate`, falling back to `window.location.assign`), with the route's `LoadingComponent` shown in the meantime. +- **Authenticated** users get an `Unauthorized` error thrown into the route's ErrorBoundary. + +Without a provider, `permission` is ignored and the route renders as before. Existing `onBefore*PageRendered` callbacks keep working unchanged alongside the provider. + +## Server-side identity + +Pass a server auth provider to `stack()`. The identity is resolved lazily and **at most once per request**, no matter how many hooks read it: + +```ts +import { stack, getRequestIdentity } from "@btst/stack/api"; +import type { StackServerAuthProvider } from "@btst/stack/api"; +import { auth } from "@/lib/auth"; // better-auth server instance + +const serverAuthProvider: StackServerAuthProvider = { + getIdentity: async ({ headers }) => { + const session = await auth.api.getSession({ headers }); + return session?.user ?? null; + }, +}; + +export const myStack = stack({ + basePath: "/api/data", + adapter: memoryAdapter, + auth: serverAuthProvider, + plugins: { + blog: blogBackendPlugin({ + hooks: { + onBeforeCreatePost: async (data, ctx) => { + const identity = await getRequestIdentity(ctx.headers); + if (!identity) throw new Error("Unauthorized"); + }, + }, + }), + }, +}); +``` + +`getRequestIdentity(headers)` returns `null` when no auth provider is configured, outside a request handled by `stack().handler`, or when the user is unauthenticated — so hooks written against it are safe in all setups. + + + +## Types + + + + diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json index e602b2225..50f9178e7 100644 --- a/docs/content/docs/meta.json +++ b/docs/content/docs/meta.json @@ -26,6 +26,7 @@ "---[Database]Databases---", "databases/adapters", "---[BookOpenCheck]Concepts---", + "auth", "cli", "api-reference", "standalone-components", diff --git a/packages/stack/package.json b/packages/stack/package.json index 38a2e5778..2dca0c632 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -828,9 +828,9 @@ "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "react-error-boundary": ">=4.0.0", - "react-router": ">=7.0.0", "react-hook-form": ">=7.55.0", "react-markdown": ">=9.1.0", + "react-router": ">=7.0.0", "rehype-highlight": ">=7.0.0", "rehype-katex": ">=7.0.0", "rehype-raw": ">=7.0.0", @@ -875,6 +875,7 @@ "@workspace/ui": "workspace:*", "ai": "^5.0.94", "better-call": "catalog:", + "jsdom": "^28.1.0", "knip": "^5.61.2", "next": "16.0.10", "react": "^19.2.7", diff --git a/packages/stack/src/__tests__/auth-provider.test.tsx b/packages/stack/src/__tests__/auth-provider.test.tsx new file mode 100644 index 000000000..1b69bb667 --- /dev/null +++ b/packages/stack/src/__tests__/auth-provider.test.tsx @@ -0,0 +1,369 @@ +// @vitest-environment jsdom +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { renderToString } from "react-dom/server"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { ComposedRoute } from "../client/components"; +import { + CanAccess, + StackProvider, + useCan, + useIdentity, + type StackAuthProvider, +} from "../context"; + +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +let container: HTMLDivElement; +let root: Root; + +beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + root = createRoot(container); +}); + +afterEach(async () => { + await act(async () => { + root.unmount(); + }); + container.remove(); + vi.restoreAllMocks(); +}); + +async function render(ui: React.ReactElement) { + await act(async () => { + root.render(ui); + }); +} + +function provider( + overrides: Partial = {}, +): StackAuthProvider { + return { + getIdentity: () => ({ id: "user-1", name: "Test User" }), + ...overrides, + }; +} + +function Providers({ + auth, + router, + children, +}: { + auth?: StackAuthProvider; + router?: { navigate?: (path: string) => void }; + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} + +describe("useIdentity", () => { + it("returns null identity and not pending without an auth provider", async () => { + let captured: any; + function Probe() { + captured = useIdentity(); + return null; + } + await render( + + + , + ); + + expect(captured.identity).toBeNull(); + expect(captured.isPending).toBe(false); + }); + + it("resolves identity from the provider", async () => { + let captured: any; + function Probe() { + captured = useIdentity(); + return null; + } + await render( + + + , + ); + + expect(captured.identity).toEqual({ id: "user-1", name: "Test User" }); + expect(captured.isPending).toBe(false); + }); + + it("resolves to null identity when getIdentity throws", async () => { + vi.spyOn(console, "error").mockImplementation(() => {}); + let captured: any; + function Probe() { + captured = useIdentity(); + return null; + } + await render( + Promise.reject(new Error("boom")), + })} + > + + , + ); + + expect(captured.identity).toBeNull(); + expect(captured.isPending).toBe(false); + }); +}); + +describe("useCan", () => { + it("allows everything without an auth provider", async () => { + let captured: any; + function Probe() { + captured = useCan({ resource: "blog:post", action: "delete" }); + return null; + } + await render( + + + , + ); + + expect(captured).toEqual({ can: true, isPending: false }); + }); + + it("allows everything when the provider has no can()", async () => { + let captured: any; + function Probe() { + captured = useCan({ resource: "blog:post", action: "delete" }); + return null; + } + await render( + + + , + ); + + expect(captured).toEqual({ can: true, isPending: false }); + }); + + it("passes the resolved identity and params to can()", async () => { + const can = vi.fn().mockResolvedValue(true); + let captured: any; + function Probe() { + captured = useCan({ + resource: "blog:post", + action: "delete", + params: { id: "p1" }, + }); + return null; + } + await render( + + + , + ); + + expect(can).toHaveBeenCalledWith({ + resource: "blog:post", + action: "delete", + params: { id: "p1" }, + identity: { id: "user-1", name: "Test User" }, + }); + expect(captured).toEqual({ can: true, isPending: false }); + }); + + it("denies when can() resolves false", async () => { + let captured: any; + function Probe() { + captured = useCan({ resource: "blog:post", action: "delete" }); + return null; + } + await render( + false })}> + + , + ); + + expect(captured).toEqual({ can: false, isPending: false }); + }); + + it("denies when can() throws", async () => { + vi.spyOn(console, "error").mockImplementation(() => {}); + let captured: any; + function Probe() { + captured = useCan({ resource: "blog:post", action: "delete" }); + return null; + } + await render( + Promise.reject(new Error("boom")) })} + > + + , + ); + + expect(captured).toEqual({ can: false, isPending: false }); + }); +}); + +describe("CanAccess", () => { + it("renders children without an auth provider", async () => { + await render( + + + + + , + ); + + expect(container.textContent).toBe("Delete"); + }); + + it("renders children when can() allows", async () => { + await render( + true })}> + + + + , + ); + + expect(container.textContent).toBe("Delete"); + }); + + it("hides children and renders fallback when can() denies", async () => { + await render( + false })}> + No access} + > + + + , + ); + + expect(container.textContent).toBe("No access"); + }); + + it("renders nothing by default when denied", async () => { + await render( + false })}> + + + + , + ); + + expect(container.textContent).toBe(""); + }); + + it("renders the loading node while the check is pending", async () => { + // A can() that never resolves keeps the check pending forever. + const pending = new Promise(() => {}); + await render( + pending })}> + Checking...} + > + + + , + ); + + expect(container.textContent).toBe("Checking..."); + }); +}); + +describe("SSR parity (no auth provider)", () => { + it("renderToString output is unchanged by the auth wiring", () => { + const html = renderToString( + + + child + + , + ); + expect(html).toContain("child"); + }); +}); + +describe("route gating (ComposedRoute permission)", () => { + const Page = () =>
secret page
; + const Loading = () =>
loading...
; + const ErrorUi = () =>
error page
; + + function GatedRoute() { + return ( + {}} + permission={{ resource: "blog:draft", action: "read" }} + /> + ); + } + + it("renders the page unchanged when no auth provider is configured", async () => { + await render( + + + , + ); + + expect(container.textContent).toBe("secret page"); + }); + + it("renders the page when can() allows", async () => { + await render( + true })}> + + , + ); + + expect(container.textContent).toBe("secret page"); + }); + + it("redirects unauthenticated users to loginPath via router.navigate", async () => { + const navigate = vi.fn(); + await render( + null, + can: () => false, + loginPath: "/login", + })} + router={{ navigate }} + > + + , + ); + + expect(navigate).toHaveBeenCalledWith("/login"); + // The gated page never renders while redirecting. + expect(container.textContent).toBe("loading..."); + }); + + it("throws into the route ErrorBoundary when an authenticated user is denied", async () => { + vi.spyOn(console, "error").mockImplementation(() => {}); + const navigate = vi.fn(); + await render( + false, loginPath: "/login" })} + router={{ navigate }} + > + + , + ); + + expect(navigate).not.toHaveBeenCalled(); + expect(container.textContent).toBe("error page"); + }); +}); diff --git a/packages/stack/src/__tests__/stack-auth.test.ts b/packages/stack/src/__tests__/stack-auth.test.ts new file mode 100644 index 000000000..499b4e52c --- /dev/null +++ b/packages/stack/src/__tests__/stack-auth.test.ts @@ -0,0 +1,129 @@ +import { createDbPlugin } from "@btst/db"; +import type { DBAdapter as Adapter, DatabaseDefinition } from "@btst/db"; +import { createMemoryAdapter } from "@btst/adapter-memory"; +import { describe, expect, it, vi } from "vitest"; +import { getRequestIdentity, stack } from "../api"; +import { createEndpoint, defineBackendPlugin } from "../plugins/api"; +import type { StackServerAuthProvider } from "../types"; + +const testAdapter = (db: DatabaseDefinition): Adapter => + createMemoryAdapter(db)({}); + +/** + * Minimal plugin whose route reads the request identity the way plugin + * lifecycle hooks are expected to: via getRequestIdentity(ctx.headers). + * It calls it twice to verify per-request memoization. + */ +const whoamiPlugin = defineBackendPlugin({ + name: "whoami", + dbPlugin: createDbPlugin("whoami", {}), + routes: () => ({ + whoami: createEndpoint("/whoami", { method: "GET" }, async (ctx) => { + const first = await getRequestIdentity(ctx.headers); + const second = await getRequestIdentity(ctx.headers); + return { first, second }; + }), + }), +}); + +function makeStack(auth?: StackServerAuthProvider) { + return stack({ + basePath: "/api", + plugins: { whoami: whoamiPlugin }, + adapter: testAdapter, + auth, + }); +} + +async function callWhoami(backend: { handler: (r: Request) => any }) { + const res = await backend.handler( + new Request("http://localhost/api/whoami", { + headers: { "x-user-id": "user-1" }, + }), + ); + return res.json(); +} + +describe("stack() server-side identity resolution", () => { + it("resolves identity from the auth provider and memoizes it per request", async () => { + const getIdentity = vi.fn(({ headers }: { headers: Headers }) => ({ + id: headers.get("x-user-id")!, + })); + const backend = makeStack({ getIdentity }); + + const body = await callWhoami(backend); + + expect(body.first).toEqual({ id: "user-1" }); + expect(body.second).toEqual({ id: "user-1" }); + // Two getRequestIdentity calls, one provider invocation. + expect(getIdentity).toHaveBeenCalledTimes(1); + }); + + it("resolves identity again for each new request", async () => { + const getIdentity = vi.fn(() => ({ id: "user-1" })); + const backend = makeStack({ getIdentity }); + + await callWhoami(backend); + await callWhoami(backend); + + expect(getIdentity).toHaveBeenCalledTimes(2); + }); + + it("normalizes undefined identities to null", async () => { + const backend = makeStack({ + getIdentity: () => undefined as any, + }); + + const body = await callWhoami(backend); + expect(body.first).toBeNull(); + }); + + it("passes headers and request to getIdentity", async () => { + const getIdentity = vi.fn( + (_ctx: { headers: Headers; request: Request }) => null, + ); + const backend = makeStack({ getIdentity }); + + await callWhoami(backend); + + const ctx = getIdentity.mock.calls[0]![0]; + expect(ctx.headers.get("x-user-id")).toBe("user-1"); + expect(ctx.request.url).toBe("http://localhost/api/whoami"); + }); + + it("returns null without an auth provider (handler untouched)", async () => { + const backend = makeStack(); + + const body = await callWhoami(backend); + expect(body.first).toBeNull(); + expect(body.second).toBeNull(); + }); + + it("returns null for headers outside any handled request", async () => { + const identity = await getRequestIdentity(new Headers()); + expect(identity).toBeNull(); + }); + + it("exposes the auth provider on the plugin StackContext", () => { + const auth: StackServerAuthProvider = { getIdentity: () => null }; + let seenAuth: StackServerAuthProvider | undefined; + + const probePlugin = defineBackendPlugin({ + name: "probe", + dbPlugin: createDbPlugin("probe", {}), + routes: (_adapter, context) => { + seenAuth = context?.auth; + return {}; + }, + }); + + stack({ + basePath: "/api", + plugins: { probe: probePlugin }, + adapter: testAdapter, + auth, + }); + + expect(seenAuth).toBe(auth); + }); +}); diff --git a/packages/stack/src/api/index.ts b/packages/stack/src/api/index.ts index 1934dba79..62090d3dd 100644 --- a/packages/stack/src/api/index.ts +++ b/packages/stack/src/api/index.ts @@ -6,10 +6,70 @@ import type { PluginApis, StackContext, } from "../types"; +import type { + StackIdentity, + StackServerAuthProvider, +} from "../shared/auth-types"; import { defineDb } from "@btst/db"; export { toNodeHandler } from "better-call/node"; +/** + * Lazy, memoized identity resolvers keyed by the request's `Headers` + * instance. better-call passes the same `Headers` object from the incoming + * `Request` into every endpoint context, so lifecycle hooks can look the + * identity up via `getRequestIdentity(ctx.headers)`. Entries are + * garbage-collected with the request. + */ +const identityResolvers = new WeakMap< + Headers, + () => Promise +>(); + +function registerIdentityResolver( + request: Request, + auth: StackServerAuthProvider, +): void { + let cached: Promise | undefined; + identityResolvers.set(request.headers, () => { + cached ??= Promise.resolve( + auth.getIdentity({ headers: request.headers, request }), + ).then((identity) => identity ?? null); + return cached; + }); +} + +/** + * Returns the identity of the request that carried these headers, as resolved + * by the `auth` provider configured on `stack()`. + * + * The provider's `getIdentity` runs at most once per request (memoized), no + * matter how many hooks call this. Returns `null` when no auth provider is + * configured, when called outside a request handled by `stack().handler`, or + * when the user is unauthenticated. + * + * @example + * ```ts + * import { getRequestIdentity } from "@btst/stack/api"; + * + * const blogBackend = blogBackendPlugin({ + * hooks: { + * onBeforeCreatePost: async (data, ctx) => { + * const identity = await getRequestIdentity(ctx.headers); + * if (!identity) throw new Error("Unauthorized"); + * }, + * }, + * }); + * ``` + */ +export async function getRequestIdentity( + // Optional because better-call types endpoint `ctx.headers` as optional. + headers: Headers | undefined, +): Promise { + const resolve = headers ? identityResolvers.get(headers) : undefined; + return resolve ? resolve() : null; +} + /** * Creates the backend library with plugin support * @@ -37,7 +97,7 @@ export function stack< >( config: BackendLibConfig, ): BackendLib> { - const { plugins, adapter, dbSchema, basePath } = config; + const { plugins, adapter, dbSchema, basePath, auth } = config; // Collect all routes from all plugins with type-safe prefixed keys const allRoutes = {} as TRoutes; @@ -57,6 +117,7 @@ export function stack< plugins, basePath, adapter: adapterInstance, + auth, }; for (const [pluginKey, plugin] of Object.entries(plugins)) { @@ -83,8 +144,18 @@ export function stack< basePath: basePath, }); + // With an auth provider, register a per-request identity resolver before + // dispatch so hooks can call getRequestIdentity(ctx.headers). Without one, + // the handler is returned untouched. + const handler = auth + ? (request: Request) => { + registerIdentityResolver(request, auth); + return router.handler(request); + } + : router.handler; + return { - handler: router.handler, + handler, router, dbSchema: betterDbSchema, adapter: adapterInstance, @@ -99,3 +170,9 @@ export type { PluginApis, StackContext, } from "../types"; + +export type { + CanParams, + StackIdentity, + StackServerAuthProvider, +} from "../shared/auth-types"; diff --git a/packages/stack/src/client/components/compose.tsx b/packages/stack/src/client/components/compose.tsx index b0a8a86fc..72af66f69 100644 --- a/packages/stack/src/client/components/compose.tsx +++ b/packages/stack/src/client/components/compose.tsx @@ -1,8 +1,11 @@ "use client"; -import React, { Suspense, type ErrorInfo } from "react"; +import React, { Suspense, useEffect, type ErrorInfo } from "react"; import { type FallbackProps } from "react-error-boundary"; import type { createRouter } from "@btst/yar"; +import { useAuthContext, useCan } from "../../context/auth"; +import { useStackOrNull } from "../../context/provider"; +import type { CanParams } from "../../shared/auth-types"; import { ErrorBoundary } from "./error-boundary"; /** @@ -60,6 +63,65 @@ export function RouteRenderer({ ); } +/** + * Route-level permission gate used by `ComposedRoute` when a `permission` + * is declared. + * + * - Without an auth provider on `StackProvider`, renders children unchanged. + * - While the identity/permission check is pending, renders the route's + * `LoadingComponent` so gated content never flashes. + * - On deny: unauthenticated users are redirected to the provider's + * `loginPath` (via the top-level router's `navigate`, falling back to + * `window.location.assign`); authenticated users get an `Unauthorized` + * error thrown into the route's ErrorBoundary. + */ +function RouteGuard({ + permission, + LoadingComponent, + children, +}: { + permission: CanParams; + LoadingComponent?: React.ComponentType; + children: React.ReactNode; +}) { + const auth = useAuthContext(); + const stack = useStackOrNull(); + const { can, isPending } = useCan(permission); + + const identity = auth?.identity ?? null; + const loginPath = auth?.provider.loginPath; + const navigate = stack?.router?.navigate; + + const shouldRedirect = + !!auth && !isPending && !can && !identity && !!loginPath; + + useEffect(() => { + if (!shouldRedirect || !loginPath) return; + if (navigate) { + void navigate(loginPath); + } else if (typeof window !== "undefined") { + window.location.assign(loginPath); + } + }, [shouldRedirect, loginPath, navigate]); + + // No auth provider configured: gating is disabled, behave exactly as before. + if (!auth) { + return <>{children}; + } + + if (isPending || shouldRedirect) { + return LoadingComponent ? : null; + } + + if (can) { + return <>{children}; + } + + throw new Error( + `Unauthorized: cannot ${permission.action} ${permission.resource}`, + ); +} + /** * Renders a route with Suspense and ErrorBoundary wrappers. * Handles loading states, error boundaries, and not-found scenarios for a single route. @@ -75,6 +137,9 @@ export function RouteRenderer({ * a prop named `params` or `query` intentionally takes precedence over the * router-extracted values. Only pass trusted, framework-controlled values. * @param onError - Error handler callback for the error boundary + * @param permission - Optional route-level permission requirement (e.g. + * `{ resource: "blog:draft", action: "read" }`). Only enforced when an + * auth provider is configured on `StackProvider`; see `RouteGuard`. */ export function ComposedRoute({ path, @@ -85,6 +150,7 @@ export function ComposedRoute({ NotFoundComponent, props, onError, + permission, }: { path: string; PageComponent: React.ComponentType; @@ -94,9 +160,16 @@ export function ComposedRoute({ NotFoundComponent?: React.ComponentType<{ message: string }>; props?: any; onError: (error: Error, info: ErrorInfo) => void; + permission?: CanParams; }) { if (PageComponent) { - const content = ; + const content = permission ? ( + + + + ) : ( + + ); // Always provide the same fallback on server and client — using // `typeof window !== "undefined"` here would produce a different JSX tree // on each side, shifting React's useId() counter and causing hydration diff --git a/packages/stack/src/context/auth.tsx b/packages/stack/src/context/auth.tsx new file mode 100644 index 000000000..3cd5f55f8 --- /dev/null +++ b/packages/stack/src/context/auth.tsx @@ -0,0 +1,221 @@ +"use client"; +import { + createContext, + useCallback, + useContext, + useEffect, + useState, + type ReactNode, +} from "react"; +import type { + CanParams, + StackAuthProvider, + StackIdentity, +} from "../shared/auth-types"; + +interface AuthContextValue { + provider: StackAuthProvider; + identity: StackIdentity | null; + /** True until the initial `getIdentity()` call settles */ + isPending: boolean; + /** Re-run `getIdentity()` (e.g. after login/logout) */ + refetch: () => Promise; +} + +/** + * Default is `null` = no auth provider configured. Every consumer treats that + * as "auth disabled": identity is `null`, all permission checks pass, and + * `` renders its children — preserving pre-auth behavior exactly. + */ +const AuthContext = createContext(null); + +/** + * Internal boundary rendered by `StackProvider` when an `auth` provider is + * configured. Resolves `getIdentity()` once on the client (effects don't run + * during SSR, so server renders see `isPending: true`) and shares the result + * with all `useIdentity()` / `useCan()` / `` consumers. + */ +export function StackAuthBoundary({ + provider, + children, +}: { + provider: StackAuthProvider; + children?: ReactNode; +}) { + const [state, setState] = useState<{ + identity: StackIdentity | null; + isPending: boolean; + }>({ identity: null, isPending: true }); + + const refetch = useCallback(async () => { + try { + const identity = await provider.getIdentity(); + setState({ identity: identity ?? null, isPending: false }); + } catch (error) { + console.error("[btst/auth] getIdentity() failed:", error); + setState({ identity: null, isPending: false }); + } + }, [provider]); + + useEffect(() => { + void refetch(); + }, [refetch]); + + return ( + + {children} + + ); +} + +/** @internal Access the raw auth context (or `null` when no provider is set). */ +export function useAuthContext(): AuthContextValue | null { + return useContext(AuthContext); +} + +/** + * Returns the current user's identity as resolved by the auth provider + * configured on `StackProvider`. + * + * Without an auth provider, returns `{ identity: null, isPending: false }`. + * + * @example + * ```tsx + * const { identity, isPending } = useIdentity(); + * if (identity) return Hello {identity.name}; + * ``` + */ +export function useIdentity(): { + identity: StackIdentity | null; + isPending: boolean; + refetch: () => Promise; +} { + const auth = useContext(AuthContext); + + if (!auth) { + return { identity: null, isPending: false, refetch: async () => {} }; + } + + return { + identity: auth.identity, + isPending: auth.isPending, + refetch: auth.refetch, + }; +} + +type CanState = { can: boolean; isPending: boolean }; + +/** + * Checks whether the current user can perform `action` on `resource` using + * the auth provider's `can()` function. + * + * Resolves to `{ can: true, isPending: false }` immediately when no auth + * provider is configured or the provider has no `can()` function — permission + * checks are opt-in and non-breaking. + * + * While the identity or the `can()` result is still resolving, returns + * `{ can: false, isPending: true }` so callers can avoid flashing + * permission-gated UI. + * + * @example + * ```tsx + * const { can, isPending } = useCan({ resource: "blog:post", action: "delete" }); + * if (!isPending && can) return ; + * ``` + */ +export function useCan(params: CanParams): CanState { + const auth = useContext(AuthContext); + const canFn = auth?.provider.can; + const identity = auth?.identity ?? null; + const identityPending = auth?.isPending ?? false; + + const { resource, action, params: extraParams } = params; + // Serialize extra params so plain-object literals don't retrigger the + // effect on every render. + const extraParamsKey = extraParams ? JSON.stringify(extraParams) : ""; + + const [state, setState] = useState({ + can: false, + isPending: true, + }); + + useEffect(() => { + if (!canFn || identityPending) return; + + let cancelled = false; + setState({ can: false, isPending: true }); + + void (async () => { + try { + const allowed = await canFn({ + resource, + action, + ...(extraParamsKey ? { params: JSON.parse(extraParamsKey) } : {}), + identity, + }); + if (!cancelled) setState({ can: allowed, isPending: false }); + } catch (error) { + console.error("[btst/auth] can() failed:", error); + if (!cancelled) setState({ can: false, isPending: false }); + } + })(); + + return () => { + cancelled = true; + }; + }, [canFn, identity, identityPending, resource, action, extraParamsKey]); + + // No provider or no can() function: always allowed, never pending. + if (!auth || !canFn) { + return { can: true, isPending: false }; + } + + if (identityPending) { + return { can: false, isPending: true }; + } + + return state; +} + +/** + * Element-level permission gate (Refine's `` pattern). + * + * - Without an auth provider configured, always renders `children`. + * - While the check is pending, renders `loading` (default `null`) to avoid + * flashing gated UI. + * - Renders `children` when `can()` allows, `fallback` (default `null`) + * otherwise. + * + * @example + * ```tsx + * + * + * + * ``` + */ +export function CanAccess({ + resource, + action, + params, + fallback = null, + loading = null, + children, +}: CanParams & { + /** Rendered when access is denied (default `null`) */ + fallback?: ReactNode; + /** Rendered while the permission check is pending (default `null`) */ + loading?: ReactNode; + children?: ReactNode; +}) { + const { can, isPending } = useCan({ resource, action, params }); + + if (isPending) return <>{loading}; + return <>{can ? children : fallback}; +} diff --git a/packages/stack/src/context/index.ts b/packages/stack/src/context/index.ts index 1e4fdb3d8..6687d7309 100644 --- a/packages/stack/src/context/index.ts +++ b/packages/stack/src/context/index.ts @@ -1,2 +1,9 @@ export * from "./provider"; export * from "./router"; +export { CanAccess, useCan, useIdentity } from "./auth"; +export type { + CanParams, + StackAuthProvider, + StackIdentity, + StackServerAuthProvider, +} from "../shared/auth-types"; diff --git a/packages/stack/src/context/provider.tsx b/packages/stack/src/context/provider.tsx index 1adfc4217..f88574a8e 100644 --- a/packages/stack/src/context/provider.tsx +++ b/packages/stack/src/context/provider.tsx @@ -1,5 +1,7 @@ "use client"; import { createContext, useContext, type ReactNode } from "react"; +import type { StackAuthProvider } from "../shared/auth-types"; +import { StackAuthBoundary } from "./auth"; import type { StackApiConfig, StackRouter, @@ -152,12 +154,20 @@ export function StackProvider< basePath, router, api, + auth, }: { children?: ReactNode; overrides?: StackProviderOverrides; basePath: string; router?: StackRouterConfig; api?: StackApiConfig; + /** + * Optional auth provider. When set, `useIdentity()` / `useCan()` and + * `` resolve identity and permissions through it. When omitted, + * behavior is identical to before: identity is `null` and all permission + * checks pass. + */ + auth?: StackAuthProvider; }) { const staticRouter = resolveStaticRouter(router); const value: Omit, "router"> = { @@ -166,6 +176,12 @@ export function StackProvider< api, }; + const content = auth ? ( + {children} + ) : ( + children + ); + if (router?.useRouter) { return ( - {children} + {content} ); } return ( - {children} + {content} ); } @@ -214,6 +230,19 @@ export function useStack< return context; } +/** + * Like `useStack`, but returns `null` instead of throwing when rendered + * outside a `StackProvider`. + * + * @internal Used by core components (e.g. route gating) that must not change + * behavior for consumers rendering outside the provider. + */ +export function useStackOrNull< + TPluginOverrides extends Record = Record, +>() { + return useContext(StackContext) as StackContextValue | null; +} + // Helper type: merge TOverrides with TDefaults, making defaulted properties required type OverridesResult = undefined extends TDefaults ? TOverrides diff --git a/packages/stack/src/shared/auth-types.ts b/packages/stack/src/shared/auth-types.ts new file mode 100644 index 000000000..96d859648 --- /dev/null +++ b/packages/stack/src/shared/auth-types.ts @@ -0,0 +1,98 @@ +/** + * Shared auth contract types used by both the client provider + * (`@btst/stack/context`) and the backend (`@btst/stack/api`). + * + * This module is intentionally type-only so it can be imported from server + * and client code alike. + */ + +/** + * The identity of the current user as resolved by an auth provider. + * Extra provider-specific fields are allowed. + */ +export interface StackIdentity { + /** Unique user id */ + id: string; + /** Display name */ + name?: string; + /** Email address */ + email?: string; + /** Avatar image URL */ + image?: string; + /** Additional provider-specific fields */ + [key: string]: unknown; +} + +/** + * A permission check request: "can the current user perform `action` on + * `resource`?" (e.g. resource `"blog:post"`, action `"delete"`). + */ +export interface CanParams { + /** The resource being accessed (e.g. "blog:post", "comments:moderate") */ + resource: string; + /** The action being performed (e.g. "read", "create", "delete") */ + action: string; + /** Optional extra parameters (e.g. the specific record id) */ + params?: Record; +} + +/** + * Client-side auth provider, passed to `StackProvider` via the `auth` prop. + * + * All fields besides `getIdentity` are optional: + * - Without `can`, every permission check resolves to `true`. + * - Without `loginPath`, denied route access is not redirected. + * + * @example + * ```tsx + * const authProvider: StackAuthProvider = { + * getIdentity: () => authClient.getSession().then((s) => s?.user ?? null), + * can: ({ resource, action }) => checkPermission(resource, action), + * loginPath: "/login", + * }; + * + * + * ``` + */ +export interface StackAuthProvider { + /** + * Resolve the current user's identity. Return `null` when unauthenticated. + */ + getIdentity: () => Promise | StackIdentity | null; + /** + * Permission check. When omitted, all `useCan()` / `` checks + * resolve to `true`. + */ + can?: ( + params: CanParams & { identity: StackIdentity | null }, + ) => Promise | boolean; + /** + * Path unauthenticated users are redirected to when route-level permission + * checks deny access (e.g. "/login"). + */ + loginPath?: string; +} + +/** + * Server-side auth provider, passed to `stack()` via the `auth` config option. + * `stack()` resolves the identity lazily and at most once per request; plugin + * lifecycle hooks can read it via `getRequestIdentity(headers)` from + * `@btst/stack/api`. + */ +export interface StackServerAuthProvider { + /** + * Resolve the identity for an incoming request (e.g. from a session + * cookie). Return `null` when unauthenticated. + */ + getIdentity: (ctx: { + headers: Headers; + request: Request; + }) => Promise | StackIdentity | null; + /** + * Optional server-side permission check, for consumers and plugins that + * want to share one `can` implementation across lifecycle hooks. + */ + can?: ( + params: CanParams & { identity: StackIdentity | null; headers: Headers }, + ) => Promise | boolean; +} diff --git a/packages/stack/src/types.ts b/packages/stack/src/types.ts index 6639291ba..31dac1339 100644 --- a/packages/stack/src/types.ts +++ b/packages/stack/src/types.ts @@ -5,6 +5,14 @@ import type { DbPlugin, } from "@btst/db"; import type { Endpoint, Router } from "better-call"; +import type { StackServerAuthProvider } from "./shared/auth-types"; + +export type { + CanParams, + StackAuthProvider, + StackIdentity, + StackServerAuthProvider, +} from "./shared/auth-types"; /** * Context passed to backend plugins during route creation @@ -17,6 +25,8 @@ export interface StackContext { basePath: string; /** The database adapter */ adapter: Adapter; + /** The server-side auth provider, when configured on `stack()` */ + auth?: StackServerAuthProvider; } /** @@ -134,6 +144,13 @@ export interface BackendLibConfig< dbSchema?: DatabaseDefinition; plugins: TPlugins; adapter: (db: DatabaseDefinition) => Adapter; + /** + * Optional server-side auth provider. When set, `stack()` resolves the + * request identity lazily and at most once per request; plugin lifecycle + * hooks and route handlers can read it via `getRequestIdentity(headers)` + * from `@btst/stack/api`. When omitted, behavior is unchanged. + */ + auth?: StackServerAuthProvider; } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 545c39747..8a6d7d82f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -236,7 +236,7 @@ importers: version: 0.522.0(react@19.2.7) next: specifier: 16.0.10 - version: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: specifier: 19.2.7 version: 19.2.7 @@ -340,7 +340,7 @@ importers: dependencies: '@btst/db': specifier: 2.2.2 - version: 2.2.2(84c0473dacd82373aa0e1c019674ae07) + version: 2.2.2(706a9ce1fcc616cc33147c0a84ffbdf8) '@hookform/resolvers': specifier: '>=5.0.0' version: 5.2.2(react-hook-form@7.66.1(react@19.2.7)) @@ -440,7 +440,7 @@ importers: version: 3.1011.0 '@btst/adapter-memory': specifier: 2.2.2 - version: 2.2.2(1cc8778580309809cf6cdfc6c185225b) + version: 2.2.2(018b1bc68345b791db4bf45b13a5d907) '@btst/yar': specifier: 1.3.0 version: 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react@19.2.7) @@ -468,12 +468,15 @@ importers: better-call: specifier: 'catalog:' version: 1.3.6(zod@4.4.3) + jsdom: + specifier: ^28.1.0 + version: 28.1.0(@noble/hashes@2.0.1) knip: specifier: ^5.61.2 - version: 5.86.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.0)(typescript@5.9.3) + version: 5.86.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@24.12.0)(typescript@5.9.3) next: specifier: 16.0.10 - version: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: specifier: 19.2.7 version: 19.2.7 @@ -503,7 +506,7 @@ importers: version: 3.6.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3)) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) zod: specifier: 4.4.3 version: 4.4.3 @@ -798,13 +801,13 @@ importers: version: 1.7.0(react@19.2.7) next: specifier: 16.0.10 - version: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) nuqs: specifier: ^2.8.9 - version: 2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) + version: 2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) react: specifier: 19.2.7 version: 19.2.7 @@ -6040,9 +6043,6 @@ packages: '@types/node@24.12.0': resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==} - '@types/node@25.5.0': - resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} - '@types/object-hash@3.0.6': resolution: {integrity: sha512-fOBV8C1FIu2ELinoILQ+ApxcUKz4ngq+IWUYrxSGjXzzjUALijilampwkMgEtJ+h2njAW3pi853QpzNVCHB73w==} @@ -6220,6 +6220,7 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@unrs/resolver-binding-android-arm-eabi@1.11.1': resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} @@ -6798,7 +6799,7 @@ packages: basic-ftp@5.0.5: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} - deprecated: Security vulnerability fixed in 5.2.0, please upgrade + deprecated: Security vulnerability fixed in 5.2.1, please upgrade better-auth@1.6.16: resolution: {integrity: sha512-YlBITnH3LIBRD+JpR1XRIToJAVVpoQvZzRc4sm5W0/bnPZKLbsmtXbVWJF3ypo9TVnF6geczJKprG/CsWT07Wg==} @@ -11574,9 +11575,6 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - undici-types@7.8.0: resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} @@ -13178,11 +13176,11 @@ snapshots: dependencies: css-tree: 3.2.1 - '@btst/adapter-memory@2.2.2(1cc8778580309809cf6cdfc6c185225b)': + '@btst/adapter-memory@2.2.2(018b1bc68345b791db4bf45b13a5d907)': dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - '@btst/db': 2.2.2(84c0473dacd82373aa0e1c019674ae07) - better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) + '@btst/db': 2.2.2(706a9ce1fcc616cc33147c0a84ffbdf8) + better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) transitivePeerDependencies: - '@better-auth/utils' - '@better-fetch/fetch' @@ -13246,10 +13244,10 @@ snapshots: - vitest - vue - '@btst/db@2.2.2(7aa0e8a71fdebdc1d52576bb8fea318f)': + '@btst/db@2.2.2(706a9ce1fcc616cc33147c0a84ffbdf8)': dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - better-auth: 1.6.16(fb120b41aed529c3fb40587bdf11e900) + better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) transitivePeerDependencies: - '@better-auth/utils' - '@better-fetch/fetch' @@ -13279,10 +13277,10 @@ snapshots: - vitest - vue - '@btst/db@2.2.2(84c0473dacd82373aa0e1c019674ae07)': + '@btst/db@2.2.2(7aa0e8a71fdebdc1d52576bb8fea318f)': dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) - better-auth: 1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)) + better-auth: 1.6.16(fb120b41aed529c3fb40587bdf11e900) transitivePeerDependencies: - '@better-auth/utils' - '@better-fetch/fetch' @@ -14202,14 +14200,6 @@ snapshots: optionalDependencies: '@types/node': 24.12.0 - '@inquirer/confirm@5.1.21(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.0) - '@inquirer/type': 3.0.10(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - optional: true - '@inquirer/core@10.3.2(@types/node@22.20.0)': dependencies: '@inquirer/ansi': 1.0.2 @@ -14237,20 +14227,6 @@ snapshots: optionalDependencies: '@types/node': 24.12.0 - '@inquirer/core@10.3.2(@types/node@25.5.0)': - dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.0) - cli-width: 4.1.0 - mute-stream: 2.0.0 - signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 25.5.0 - optional: true - '@inquirer/external-editor@1.0.3(@types/node@20.19.25)': dependencies: chardet: 2.1.1 @@ -14269,11 +14245,6 @@ snapshots: optionalDependencies: '@types/node': 24.12.0 - '@inquirer/type@3.0.10(@types/node@25.5.0)': - optionalDependencies: - '@types/node': 25.5.0 - optional: true - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -17662,19 +17633,19 @@ snapshots: - vite-plugin-solid - webpack - '@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tanstack/react-start-client': 1.166.25(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tanstack/react-start-server': 1.166.25(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tanstack/router-utils': 1.162.2 '@tanstack/start-client-core': 1.167.9 - '@tanstack/start-plugin-core': 1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.9(srvx@0.11.20))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/start-plugin-core': 1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.9(srvx@0.11.20))(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) '@tanstack/start-server-core': 1.167.9(crossws@0.4.9(srvx@0.11.20)) pathe: 2.0.3 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@rsbuild/core' - crossws @@ -17739,7 +17710,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/router-plugin@1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) @@ -17756,7 +17727,7 @@ snapshots: zod: 3.25.76 optionalDependencies: '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color optional: true @@ -17834,7 +17805,7 @@ snapshots: - vite-plugin-solid - webpack - '@tanstack/start-plugin-core@1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.9(srvx@0.11.20))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/start-plugin-core@1.167.17(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.9(srvx@0.11.20))(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.29.7 @@ -17842,7 +17813,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.40 '@tanstack/router-core': 1.168.9 '@tanstack/router-generator': 1.166.24 - '@tanstack/router-plugin': 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/router-plugin': 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) '@tanstack/router-utils': 1.161.6 '@tanstack/start-client-core': 1.167.9 '@tanstack/start-server-core': 1.167.9(crossws@0.4.9(srvx@0.11.20)) @@ -17854,8 +17825,8 @@ snapshots: srvx: 0.11.16 tinyglobby: 0.2.17 ufo: 1.6.4 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) - vitefu: 1.1.3(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + vite: 7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vitefu: 1.1.3(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) xmlbuilder2: 4.0.3 zod: 3.25.76 transitivePeerDependencies: @@ -18313,10 +18284,6 @@ snapshots: dependencies: undici-types: 7.16.0 - '@types/node@25.5.0': - dependencies: - undici-types: 7.18.2 - '@types/object-hash@3.0.6': {} '@types/prismjs@1.26.5': {} @@ -18731,7 +18698,7 @@ snapshots: '@vercel/analytics@1.6.1(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(vue@3.5.24(typescript@5.9.3))': optionalDependencies: - next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 vue: 3.5.24(typescript@5.9.3) @@ -18792,15 +18759,6 @@ snapshots: msw: 2.12.10(@types/node@24.12.0)(typescript@5.9.3) vite: 7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - '@vitest/mocker@3.2.4(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))': - dependencies: - '@vitest/spy': 3.2.4 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - msw: 2.12.10(@types/node@25.5.0)(typescript@5.9.3) - vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@4.1.9(msw@2.12.10(@types/node@22.20.0)(typescript@6.0.3))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.1.9 @@ -19154,7 +19112,7 @@ snapshots: basic-ftp@5.0.5: {} - better-auth@1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)): + better-auth@1.6.16(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)))(mongodb@6.21.0(socks@2.8.7))(mysql2@3.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.12)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.24(typescript@5.9.3)): dependencies: '@better-auth/core': 1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1) '@better-auth/drizzle-adapter': 1.6.16(@better-auth/core@1.6.16(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.2.2)(@opentelemetry/api@1.9.0)(better-call@1.3.6(zod@4.4.3))(jose@6.2.0)(kysely@0.29.2)(nanostores@1.1.1))(@better-auth/utils@0.4.1) @@ -19175,15 +19133,15 @@ snapshots: zod: 4.4.3 optionalDependencies: '@prisma/client': 6.19.0(prisma@7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3) - '@tanstack/react-start': 1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/react-start': 1.167.16(crossws@0.4.9(srvx@0.11.20))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) mongodb: 6.21.0(socks@2.8.7) mysql2: 3.15.3 - next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) prisma: 7.5.0(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) solid-js: 1.9.12 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) vue: 3.5.24(typescript@5.9.3) transitivePeerDependencies: - '@cloudflare/workers-types' @@ -20263,7 +20221,7 @@ snapshots: '@typescript-eslint/parser': 8.58.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) @@ -20290,7 +20248,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0)(eslint@8.57.1): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -20313,7 +20271,7 @@ snapshots: '@typescript-eslint/parser': 8.58.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.62.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - supports-color @@ -20918,7 +20876,7 @@ snapshots: '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@types/react': 19.2.14 lucide-react: 0.522.0(react@19.2.7) - next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) react-router: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -20959,7 +20917,7 @@ snapshots: unist-util-visit: 5.0.0 zod: 4.4.3 optionalDependencies: - next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 vite: 7.3.1(@types/node@24.0.3)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) transitivePeerDependencies: @@ -21007,7 +20965,7 @@ snapshots: tailwind-merge: 3.6.0 optionalDependencies: '@types/react': 19.2.14 - next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) tailwindcss: 4.2.2 transitivePeerDependencies: - '@mixedbread/sdk' @@ -21860,10 +21818,10 @@ snapshots: kleur@4.1.5: {} - knip@5.86.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.0)(typescript@5.9.3): + knip@5.86.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@24.12.0)(typescript@5.9.3): dependencies: '@nodelib/fs.walk': 1.2.8 - '@types/node': 25.5.0 + '@types/node': 24.12.0 fast-glob: 3.3.3 formatly: 0.3.0 jiti: 2.6.1 @@ -22680,32 +22638,6 @@ snapshots: transitivePeerDependencies: - '@types/node' - msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3): - dependencies: - '@inquirer/confirm': 5.1.21(@types/node@25.5.0) - '@mswjs/interceptors': 0.41.3 - '@open-draft/deferred-promise': 2.2.0 - '@types/statuses': 2.0.6 - cookie: 1.1.1 - graphql: 16.13.1 - headers-polyfill: 4.0.3 - is-node-process: 1.2.0 - outvariant: 1.4.3 - path-to-regexp: 6.3.0 - picocolors: 1.1.1 - rettime: 0.10.1 - statuses: 2.0.2 - strict-event-emitter: 0.5.1 - tough-cookie: 6.0.1 - type-fest: 5.4.4 - until-async: 3.0.2 - yargs: 17.7.2 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - '@types/node' - optional: true - mute-stream@0.0.8: {} mute-stream@2.0.0: {} @@ -22749,7 +22681,33 @@ snapshots: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - next@16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + next@16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + '@next/env': 16.0.10 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001784 + postcss: 8.4.31 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.7) + optionalDependencies: + '@next/swc-darwin-arm64': 16.0.10 + '@next/swc-darwin-x64': 16.0.10 + '@next/swc-linux-arm64-gnu': 16.0.10 + '@next/swc-linux-arm64-musl': 16.0.10 + '@next/swc-linux-x64-gnu': 16.0.10 + '@next/swc-linux-x64-musl': 16.0.10 + '@next/swc-win32-arm64-msvc': 16.0.10 + '@next/swc-win32-x64-msvc': 16.0.10 + '@opentelemetry/api': 1.9.0 + '@playwright/test': 1.56.1 + babel-plugin-react-compiler: 1.0.0 + sharp: 0.34.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: '@next/env': 16.0.10 '@swc/helpers': 0.5.15 @@ -22910,13 +22868,13 @@ snapshots: dependencies: boolbase: 1.0.0 - nuqs@2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7): + nuqs@2.8.9(@tanstack/react-router@1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(next@16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7): dependencies: '@standard-schema/spec': 1.0.0 react: 19.2.7 optionalDependencies: '@tanstack/react-router': 1.168.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - next: 16.0.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.0.10(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react-router: 7.13.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) nypm@0.6.2: @@ -24869,6 +24827,13 @@ snapshots: dependencies: inline-style-parser: 0.2.7 + styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.7): + dependencies: + client-only: 0.0.1 + react: 19.2.7 + optionalDependencies: + '@babel/core': 7.29.0 + styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.7): dependencies: client-only: 0.0.1 @@ -25236,8 +25201,6 @@ snapshots: undici-types@7.16.0: {} - undici-types@7.18.2: {} - undici-types@7.8.0: {} undici@5.29.0: @@ -25516,27 +25479,6 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - vite-tsconfig-paths@5.1.4(typescript@6.0.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): dependencies: debug: 4.4.3 @@ -25613,29 +25555,13 @@ snapshots: tsx: 4.22.4 yaml: 2.8.2 - vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.6 - rollup: 4.53.2 - tinyglobby: 0.2.17 - optionalDependencies: - '@types/node': 25.5.0 - fsevents: 2.3.3 - jiti: 2.7.0 - lightningcss: 1.32.0 - tsx: 4.21.0 - yaml: 2.8.2 - vitefu@1.1.3(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): optionalDependencies: vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2) - vitefu@1.1.3(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)): + vitefu@1.1.3(vite@7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)): optionalDependencies: - vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@24.12.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) optional: true vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2): @@ -25724,49 +25650,6 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.7.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2): - dependencies: - '@types/chai': 5.2.3 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.12.10(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - debug: 4.4.3 - expect-type: 1.2.2 - magic-string: 0.30.21 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/debug': 4.1.12 - '@types/node': 25.5.0 - jsdom: 28.1.0(@noble/hashes@2.0.1) - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - vitest@4.1.9(@opentelemetry/api@1.9.0)(@types/node@22.20.0)(jsdom@28.1.0(@noble/hashes@2.0.1))(msw@2.12.10(@types/node@22.20.0)(typescript@6.0.3))(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.8.2)): dependencies: '@vitest/expect': 4.1.9 From 330836a976fbda1bcda58b3a730b9ae7e9935f4a Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:57:02 +0000 Subject: [PATCH 012/380] fix: address auth provider review feedback - useCan no longer returns a result computed for a previous identity or check inputs: resolved state carries the identity/key it was computed for and anything else reads as pending, so /RouteGuard never briefly expose the prior user's permissions after refetch() - can() now receives the original params object (ref) instead of a JSON round-tripped copy, preserving Dates and other non-JSON values - RouteGuard deny throws a generic "Unauthorized" and logs the resource/action detail as a dev-only warning Co-authored-by: Cursor --- .../src/__tests__/auth-provider.test.tsx | 35 +++++++++++ .../stack/src/client/components/compose.tsx | 11 +++- packages/stack/src/context/auth.tsx | 58 +++++++++++++++---- 3 files changed, 89 insertions(+), 15 deletions(-) diff --git a/packages/stack/src/__tests__/auth-provider.test.tsx b/packages/stack/src/__tests__/auth-provider.test.tsx index 1b69bb667..6bd079a81 100644 --- a/packages/stack/src/__tests__/auth-provider.test.tsx +++ b/packages/stack/src/__tests__/auth-provider.test.tsx @@ -189,6 +189,41 @@ describe("useCan", () => { expect(captured).toEqual({ can: false, isPending: false }); }); + it("does not return a stale result after the identity changes", async () => { + // First identity resolves to an admin whose can() allows; after + // refetch() the identity switches to a user whose can() never + // resolves — the hook must report pending, never the admin's `true`. + let currentUser = { id: "admin" }; + const neverResolves = new Promise(() => {}); + const auth = provider({ + getIdentity: () => currentUser, + can: ({ identity }) => (identity?.id === "admin" ? true : neverResolves), + }); + + let capturedCan: any; + let capturedIdentity: any; + function Probe() { + capturedCan = useCan({ resource: "blog:post", action: "delete" }); + capturedIdentity = useIdentity(); + return null; + } + await render( + + + , + ); + + expect(capturedCan).toEqual({ can: true, isPending: false }); + + currentUser = { id: "viewer" }; + await act(async () => { + await capturedIdentity.refetch(); + }); + + expect(capturedIdentity.identity).toEqual({ id: "viewer" }); + expect(capturedCan).toEqual({ can: false, isPending: true }); + }); + it("denies when can() throws", async () => { vi.spyOn(console, "error").mockImplementation(() => {}); let captured: any; diff --git a/packages/stack/src/client/components/compose.tsx b/packages/stack/src/client/components/compose.tsx index 72af66f69..b16bd3332 100644 --- a/packages/stack/src/client/components/compose.tsx +++ b/packages/stack/src/client/components/compose.tsx @@ -117,9 +117,14 @@ function RouteGuard({ return <>{children}; } - throw new Error( - `Unauthorized: cannot ${permission.action} ${permission.resource}`, - ); + // Keep the thrown message generic — ErrorComponents commonly render + // error.message to end-users; the resource/action detail is dev-only. + if (process.env.NODE_ENV !== "production") { + console.warn( + `[btst/auth] RouteGuard denied: cannot ${permission.action} ${permission.resource}`, + ); + } + throw new Error("Unauthorized"); } /** diff --git a/packages/stack/src/context/auth.tsx b/packages/stack/src/context/auth.tsx index 3cd5f55f8..f302199d8 100644 --- a/packages/stack/src/context/auth.tsx +++ b/packages/stack/src/context/auth.tsx @@ -4,6 +4,7 @@ import { useCallback, useContext, useEffect, + useRef, useState, type ReactNode, } from "react"; @@ -112,6 +113,19 @@ export function useIdentity(): { type CanState = { can: boolean; isPending: boolean }; +/** + * A resolved `can()` result together with the inputs it was computed for. + * `useCan` only trusts it while those inputs are still current, so a change + * in identity (login/logout/user switch) or check parameters immediately + * reads as pending instead of momentarily returning the previous user's + * permission. + */ +type ResolvedCan = { + can: boolean; + forIdentity: StackIdentity | null; + forKey: string; +}; + /** * Checks whether the current user can perform `action` on `resource` using * the auth provider's `can()` function. @@ -137,40 +151,50 @@ export function useCan(params: CanParams): CanState { const identityPending = auth?.isPending ?? false; const { resource, action, params: extraParams } = params; - // Serialize extra params so plain-object literals don't retrigger the - // effect on every render. + // Serialized only for change detection: plain-object literals must not + // retrigger the effect on every render. The original object (via ref) is + // what gets passed to can(), so non-JSON-safe values survive intact. const extraParamsKey = extraParams ? JSON.stringify(extraParams) : ""; + const extraParamsRef = useRef(extraParams); + extraParamsRef.current = extraParams; + + const checkKey = `${resource}\u0000${action}\u0000${extraParamsKey}`; - const [state, setState] = useState({ - can: false, - isPending: true, - }); + const [resolved, setResolved] = useState(null); useEffect(() => { if (!canFn || identityPending) return; let cancelled = false; - setState({ can: false, isPending: true }); void (async () => { try { + const currentParams = extraParamsRef.current; const allowed = await canFn({ resource, action, - ...(extraParamsKey ? { params: JSON.parse(extraParamsKey) } : {}), + ...(currentParams ? { params: currentParams } : {}), identity, }); - if (!cancelled) setState({ can: allowed, isPending: false }); + if (!cancelled) { + setResolved({ + can: allowed, + forIdentity: identity, + forKey: checkKey, + }); + } } catch (error) { console.error("[btst/auth] can() failed:", error); - if (!cancelled) setState({ can: false, isPending: false }); + if (!cancelled) { + setResolved({ can: false, forIdentity: identity, forKey: checkKey }); + } } })(); return () => { cancelled = true; }; - }, [canFn, identity, identityPending, resource, action, extraParamsKey]); + }, [canFn, identity, identityPending, resource, action, checkKey]); // No provider or no can() function: always allowed, never pending. if (!auth || !canFn) { @@ -181,7 +205,17 @@ export function useCan(params: CanParams): CanState { return { can: false, isPending: true }; } - return state; + // Only trust a result computed for the current identity and inputs; + // anything else (including right after an identity change) is pending. + if ( + !resolved || + resolved.forIdentity !== identity || + resolved.forKey !== checkKey + ) { + return { can: false, isPending: true }; + } + + return { can: resolved.can, isPending: false }; } /** From 852da8c21be717379b3a17f8c612d13f45593e9e Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 18:10:03 +0000 Subject: [PATCH 013/380] fix: treat server getIdentity failures as unauthenticated Mirror the client StackAuthBoundary: a rejecting or throwing getIdentity resolves getRequestIdentity to null (with a logged error) instead of propagating and failing the whole request. Co-authored-by: Cursor --- .../stack/src/__tests__/stack-auth.test.ts | 31 +++++++++++++++++++ packages/stack/src/api/index.ts | 13 ++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/packages/stack/src/__tests__/stack-auth.test.ts b/packages/stack/src/__tests__/stack-auth.test.ts index 499b4e52c..cbb96142d 100644 --- a/packages/stack/src/__tests__/stack-auth.test.ts +++ b/packages/stack/src/__tests__/stack-auth.test.ts @@ -69,6 +69,37 @@ describe("stack() server-side identity resolution", () => { expect(getIdentity).toHaveBeenCalledTimes(2); }); + it("treats getIdentity failures as unauthenticated instead of rejecting", async () => { + const consoleError = vi + .spyOn(console, "error") + .mockImplementation(() => {}); + const backend = makeStack({ + getIdentity: () => Promise.reject(new Error("session lookup failed")), + }); + + const body = await callWhoami(backend); + + expect(body.first).toBeNull(); + expect(body.second).toBeNull(); + expect(consoleError).toHaveBeenCalledOnce(); + consoleError.mockRestore(); + }); + + it("treats synchronously throwing getIdentity as unauthenticated", async () => { + const consoleError = vi + .spyOn(console, "error") + .mockImplementation(() => {}); + const backend = makeStack({ + getIdentity: () => { + throw new Error("boom"); + }, + }); + + const body = await callWhoami(backend); + expect(body.first).toBeNull(); + consoleError.mockRestore(); + }); + it("normalizes undefined identities to null", async () => { const backend = makeStack({ getIdentity: () => undefined as any, diff --git a/packages/stack/src/api/index.ts b/packages/stack/src/api/index.ts index 62090d3dd..a286a10e3 100644 --- a/packages/stack/src/api/index.ts +++ b/packages/stack/src/api/index.ts @@ -32,9 +32,16 @@ function registerIdentityResolver( ): void { let cached: Promise | undefined; identityResolvers.set(request.headers, () => { - cached ??= Promise.resolve( - auth.getIdentity({ headers: request.headers, request }), - ).then((identity) => identity ?? null); + // Mirror the client-side StackAuthBoundary: a failing getIdentity is + // treated as unauthenticated (null) rather than rejecting, so hooks + // written without try/catch can't fail the whole request. + cached ??= Promise.resolve() + .then(() => auth.getIdentity({ headers: request.headers, request })) + .then((identity) => identity ?? null) + .catch((error) => { + console.error("[btst/auth] getIdentity() failed:", error); + return null; + }); return cached; }); } From afec8cd100fe0f8f57ea366c724009bc16ecb008 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 19:01:22 +0000 Subject: [PATCH 014/380] feat(core): add notify, i18n, and useListState primitives Introduce StackProvider notify/i18n props with useNotify() and useTranslate() hooks, plus URL-synced useListState for list pages. Router search params were already on StackRouter from #127. Part of #131, #132, #133. Plugin adoption deferred to #136. Co-authored-by: Cursor --- docs/content/docs/api-reference.mdx | 20 +++ packages/stack/scripts/extract-i18n-keys.ts | 47 ++++++ .../src/__tests__/i18n-provider.test.tsx | 99 ++++++++++++ .../stack/src/__tests__/list-state.test.ts | 89 +++++++++++ .../src/__tests__/notify-provider.test.tsx | 100 ++++++++++++ .../src/__tests__/use-list-state.test.tsx | 117 ++++++++++++++ .../stack/src/client/hooks/use-list-state.ts | 109 +++++++++++++ packages/stack/src/client/index.ts | 12 ++ packages/stack/src/context/i18n.tsx | 57 +++++++ packages/stack/src/context/index.ts | 7 + packages/stack/src/context/notify.tsx | 66 ++++++++ packages/stack/src/context/provider.tsx | 44 ++++-- packages/stack/src/shared/i18n-types.ts | 31 ++++ packages/stack/src/shared/interpolate.ts | 14 ++ packages/stack/src/shared/list-state.ts | 147 ++++++++++++++++++ packages/stack/src/shared/notify-types.ts | 29 ++++ 16 files changed, 975 insertions(+), 13 deletions(-) create mode 100644 packages/stack/scripts/extract-i18n-keys.ts create mode 100644 packages/stack/src/__tests__/i18n-provider.test.tsx create mode 100644 packages/stack/src/__tests__/list-state.test.ts create mode 100644 packages/stack/src/__tests__/notify-provider.test.tsx create mode 100644 packages/stack/src/__tests__/use-list-state.test.tsx create mode 100644 packages/stack/src/client/hooks/use-list-state.ts create mode 100644 packages/stack/src/context/i18n.tsx create mode 100644 packages/stack/src/context/notify.tsx create mode 100644 packages/stack/src/shared/i18n-types.ts create mode 100644 packages/stack/src/shared/interpolate.ts create mode 100644 packages/stack/src/shared/list-state.ts create mode 100644 packages/stack/src/shared/notify-types.ts diff --git a/docs/content/docs/api-reference.mdx b/docs/content/docs/api-reference.mdx index db6096c74..0f761e9e6 100644 --- a/docs/content/docs/api-reference.mdx +++ b/docs/content/docs/api-reference.mdx @@ -106,3 +106,23 @@ type Sitemap = Array; ### useBasePath + +### useNotify + + + +Notifications are configured via the `notify` prop on `StackProvider`. Without an override, `useNotify()` routes to sonner toasts. + +### useTranslate + + + +Without an `i18n` provider, `useTranslate()` returns the English default with `{{param}}` interpolation. + +### useListState + +URL-synced list state (filters, tabs, pagination) via the router's `getSearchParams` / `setSearchParams` contract. Export path: `@btst/stack/client`. + +For SSR loaders, read initial state with `parseListStateFromSearchParams(namespace, schema, requestSearchParams)`. + + diff --git a/packages/stack/scripts/extract-i18n-keys.ts b/packages/stack/scripts/extract-i18n-keys.ts new file mode 100644 index 000000000..d074391a2 --- /dev/null +++ b/packages/stack/scripts/extract-i18n-keys.ts @@ -0,0 +1,47 @@ +#!/usr/bin/env tsx +/** + * Scan plugin sources for `t("key", "Default")` / `useTranslate()` call sites. + * + * Usage: + * pnpm exec tsx packages/stack/scripts/extract-i18n-keys.ts + * + * Output: JSON map of file → [{ key, defaultValue }] for #136 migration reference. + */ +import { readFileSync, readdirSync, statSync } from "node:fs"; +import { join, relative } from "node:path"; + +const ROOT = join(import.meta.dirname, "../src/plugins"); + +const CALL_PATTERN = + /\bt\s*\(\s*["'`]([^"'`]+)["'`]\s*,\s*["'`]((?:\\.|[^"'`\\])*)["'`]/g; + +function walk(dir: string, files: string[] = []): string[] { + for (const entry of readdirSync(dir)) { + const full = join(dir, entry); + if (statSync(full).isDirectory()) { + walk(full, files); + } else if (/\.(tsx?|jsx?)$/.test(entry)) { + files.push(full); + } + } + return files; +} + +type KeyEntry = { key: string; defaultValue: string }; + +const results: Record = {}; + +for (const file of walk(ROOT)) { + const content = readFileSync(file, "utf8"); + const entries: KeyEntry[] = []; + + for (const match of content.matchAll(CALL_PATTERN)) { + entries.push({ key: match[1], defaultValue: match[2] }); + } + + if (entries.length > 0) { + results[relative(join(import.meta.dirname, ".."), file)] = entries; + } +} + +console.log(JSON.stringify(results, null, 2)); diff --git a/packages/stack/src/__tests__/i18n-provider.test.tsx b/packages/stack/src/__tests__/i18n-provider.test.tsx new file mode 100644 index 000000000..fb8e84335 --- /dev/null +++ b/packages/stack/src/__tests__/i18n-provider.test.tsx @@ -0,0 +1,99 @@ +// @vitest-environment jsdom +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + StackProvider, + useTranslate, + type StackI18nProvider, +} from "../context"; + +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +let container: HTMLDivElement; +let root: Root; + +beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + root = createRoot(container); +}); + +afterEach(async () => { + await act(async () => { + root.unmount(); + }); + container.remove(); + vi.restoreAllMocks(); +}); + +async function render(ui: React.ReactElement) { + await act(async () => { + root.render(ui); + }); +} + +describe("useTranslate", () => { + it("returns the default string when no i18n provider is configured", async () => { + let t: ReturnType | undefined; + function Probe() { + t = useTranslate(); + return null; + } + await render( + + + , + ); + + expect(t!("blog.posts.create", "Create Post")).toBe("Create Post"); + }); + + it("interpolates {{param}} placeholders in the default string", async () => { + let t: ReturnType | undefined; + function Probe() { + t = useTranslate(); + return null; + } + await render( + + + , + ); + + expect( + t!("blog.posts.deleted", "Deleted {{title}}", { title: "Hello" }), + ).toBe("Deleted Hello"); + }); + + it("delegates to the consumer translate function", async () => { + const translate = vi.fn( + (key: string, defaultValue: string, params?: Record) => + `[${key}] ${defaultValue} ${params?.title ?? ""}`.trim(), + ); + const i18n: StackI18nProvider = { translate }; + + let t: ReturnType | undefined; + function Probe() { + t = useTranslate(); + return null; + } + + await render( + + + , + ); + + const result = t!("blog.posts.deleted", "Deleted {{title}}", { + title: "Draft", + }); + + expect(translate).toHaveBeenCalledWith( + "blog.posts.deleted", + "Deleted {{title}}", + { title: "Draft" }, + ); + expect(result).toBe("[blog.posts.deleted] Deleted {{title}} Draft"); + }); +}); diff --git a/packages/stack/src/__tests__/list-state.test.ts b/packages/stack/src/__tests__/list-state.test.ts new file mode 100644 index 000000000..701cac9b4 --- /dev/null +++ b/packages/stack/src/__tests__/list-state.test.ts @@ -0,0 +1,89 @@ +import { describe, expect, it } from "vitest"; +import { + parseListStateFromSearchParams, + resolveListStateHistoryMode, + serializeListStateToSearchParams, +} from "../shared/list-state"; + +const schema = { + tab: { type: "string" as const, default: "pending" }, + page: { type: "number" as const, default: 1 }, + filter: { type: "string" as const, default: "", history: "replace" as const }, +}; + +describe("parseListStateFromSearchParams", () => { + it("returns defaults for an empty query string", () => { + expect( + parseListStateFromSearchParams("comments-moderation", schema, ""), + ).toEqual({ tab: "pending", page: 1, filter: "" }); + }); + + it("parses non-default values from URL params", () => { + const params = new URLSearchParams("tab=spam&page=3&filter=hello"); + expect( + parseListStateFromSearchParams("comments-moderation", schema, params), + ).toEqual({ tab: "spam", page: 3, filter: "hello" }); + }); + + it("falls back to defaults for invalid numbers", () => { + const params = new URLSearchParams("page=abc"); + expect( + parseListStateFromSearchParams("comments-moderation", schema, params) + .page, + ).toBe(1); + }); +}); + +describe("serializeListStateToSearchParams", () => { + it("omits fields equal to their defaults", () => { + const params = serializeListStateToSearchParams( + "comments-moderation", + schema, + { tab: "pending", page: 1, filter: "" }, + ); + expect(params.toString()).toBe(""); + }); + + it("serializes only deviating fields", () => { + const params = serializeListStateToSearchParams( + "comments-moderation", + schema, + { tab: "spam", page: 3, filter: "" }, + ); + expect(params.toString()).toBe("tab=spam&page=3"); + }); + + it("preserves unrelated params in the base query string", () => { + const base = new URLSearchParams("foo=bar"); + const params = serializeListStateToSearchParams( + "comments-moderation", + schema, + { tab: "spam", page: 1, filter: "" }, + base, + ); + expect(params.get("foo")).toBe("bar"); + expect(params.get("tab")).toBe("spam"); + expect(params.has("page")).toBe(false); + }); +}); + +describe("resolveListStateHistoryMode", () => { + it("defaults to push when no replace fields are updated", () => { + expect(resolveListStateHistoryMode(schema, { tab: "spam", page: 2 })).toBe( + false, + ); + }); + + it("uses replace when a replace-history field is updated", () => { + expect(resolveListStateHistoryMode(schema, { filter: "x" })).toBe(true); + }); + + it("honors an explicit replace option", () => { + expect(resolveListStateHistoryMode(schema, { tab: "spam" }, true)).toBe( + true, + ); + expect(resolveListStateHistoryMode(schema, { filter: "x" }, false)).toBe( + false, + ); + }); +}); diff --git a/packages/stack/src/__tests__/notify-provider.test.tsx b/packages/stack/src/__tests__/notify-provider.test.tsx new file mode 100644 index 000000000..1265ae9b3 --- /dev/null +++ b/packages/stack/src/__tests__/notify-provider.test.tsx @@ -0,0 +1,100 @@ +// @vitest-environment jsdom +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { StackProvider, useNotify, type StackNotifyProvider } from "../context"; + +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +const toastSuccess = vi.fn(); +const toastError = vi.fn(); +const toastInfo = vi.fn(); +const toastWarning = vi.fn(); + +vi.mock("sonner", () => ({ + toast: { + success: (...args: unknown[]) => toastSuccess(...args), + error: (...args: unknown[]) => toastError(...args), + info: (...args: unknown[]) => toastInfo(...args), + warning: (...args: unknown[]) => toastWarning(...args), + }, +})); + +let container: HTMLDivElement; +let root: Root; + +beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + root = createRoot(container); + toastSuccess.mockClear(); + toastError.mockClear(); + toastInfo.mockClear(); + toastWarning.mockClear(); +}); + +afterEach(async () => { + await act(async () => { + root.unmount(); + }); + container.remove(); + vi.restoreAllMocks(); +}); + +async function render(ui: React.ReactElement) { + await act(async () => { + root.render(ui); + }); +} + +describe("useNotify", () => { + it("uses sonner by default when no notify prop is configured", async () => { + let notify: StackNotifyProvider | undefined; + function Probe() { + notify = useNotify(); + return null; + } + await render( + + + , + ); + + notify!.success!("Saved"); + notify!.error!("Failed", { description: "Try again" }); + + expect(toastSuccess).toHaveBeenCalledWith("Saved", undefined); + expect(toastError).toHaveBeenCalledWith("Failed", { + description: "Try again", + }); + }); + + it("routes notifications through a custom provider", async () => { + const customSuccess = vi.fn(); + const customError = vi.fn(); + let notify: StackNotifyProvider | undefined; + + function Probe() { + notify = useNotify(); + return null; + } + + await render( + + + , + ); + + notify!.success!("Custom saved"); + notify!.error!("Custom failed"); + notify!.info!("Still sonner"); + + expect(customSuccess).toHaveBeenCalledWith("Custom saved"); + expect(customError).toHaveBeenCalledWith("Custom failed"); + expect(toastInfo).toHaveBeenCalledWith("Still sonner", undefined); + expect(toastSuccess).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/stack/src/__tests__/use-list-state.test.tsx b/packages/stack/src/__tests__/use-list-state.test.tsx new file mode 100644 index 000000000..ecc423579 --- /dev/null +++ b/packages/stack/src/__tests__/use-list-state.test.tsx @@ -0,0 +1,117 @@ +// @vitest-environment jsdom +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { useListState } from "../client/hooks/use-list-state"; +import { StackProvider } from "../context"; + +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +const schema = { + tab: { type: "string" as const, default: "pending" }, + page: { type: "number" as const, default: 1 }, + filter: { type: "string" as const, default: "", history: "replace" as const }, +}; + +function createMockRouter() { + let params = new URLSearchParams(); + const setSearchParams = vi.fn( + (next: URLSearchParams, opts?: { replace?: boolean }) => { + params = new URLSearchParams(next.toString()); + void opts; + }, + ); + return { + getSearchParams: () => new URLSearchParams(params.toString()), + setSearchParams, + }; +} + +describe("useListState", () => { + let container: HTMLDivElement; + let root: Root; + + beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + root = createRoot(container); + }); + + afterEach(async () => { + await act(async () => { + root.unmount(); + }); + container.remove(); + vi.restoreAllMocks(); + }); + + async function renderHook(onCapture: (value: unknown) => void) { + const router = createMockRouter(); + function Probe() { + const [state, setState] = useListState("comments-moderation", schema); + onCapture({ state, setState }); + return null; + } + await act(async () => { + root.render( + + + , + ); + }); + return router; + } + + it("round-trips state through the router search params", async () => { + let captured: any; + const router = await renderHook((value) => { + captured = value; + }); + + expect(captured.state).toEqual({ tab: "pending", page: 1, filter: "" }); + expect(router.setSearchParams).not.toHaveBeenCalled(); + + await act(async () => { + captured.setState({ tab: "spam", page: 3 }); + }); + + expect(captured.state).toEqual({ tab: "spam", page: 3, filter: "" }); + expect(router.setSearchParams).toHaveBeenCalledTimes(1); + const call = router.setSearchParams.mock.calls[0]; + expect(call).toBeDefined(); + const [nextParams] = call!; + expect(nextParams.toString()).toBe("tab=spam&page=3"); + }); + + it("uses replace history for rapid filter changes", async () => { + let captured: any; + const router = await renderHook((value) => { + captured = value; + }); + + await act(async () => { + captured.setState({ filter: "abc" }); + }); + + expect(router.setSearchParams).toHaveBeenCalledWith( + expect.any(URLSearchParams), + { replace: true }, + ); + }); + + it("uses push history for discrete tab changes", async () => { + let captured: any; + const router = await renderHook((value) => { + captured = value; + }); + + await act(async () => { + captured.setState({ tab: "spam" }); + }); + + expect(router.setSearchParams).toHaveBeenCalledWith( + expect.any(URLSearchParams), + { replace: false }, + ); + }); +}); diff --git a/packages/stack/src/client/hooks/use-list-state.ts b/packages/stack/src/client/hooks/use-list-state.ts new file mode 100644 index 000000000..1aa5c1597 --- /dev/null +++ b/packages/stack/src/client/hooks/use-list-state.ts @@ -0,0 +1,109 @@ +"use client"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { useStackOrNull } from "../../context/provider"; +import type { InferListState, ListStateSchema } from "../../shared/list-state"; +import { + parseListStateFromSearchParams, + resolveListStateHistoryMode, + serializeListStateToSearchParams, +} from "../../shared/list-state"; + +export type { + InferListState, + ListStateField, + ListStateSchema, +} from "../../shared/list-state"; +export { + listStateParamKey, + parseListStateFromSearchParams, + serializeListStateToSearchParams, +} from "../../shared/list-state"; + +export type SetListStateOptions = { + /** Force `replace` vs `push` history semantics for this update */ + replace?: boolean; +}; + +export type SetListState = ( + updates: + | Partial> + | ((prev: InferListState) => Partial>), + options?: SetListStateOptions, +) => void; + +/** + * Sync list UI state (filters, tabs, pagination) with URL search params. + * + * Defaults are omitted from the URL. Field-level `history: "replace"` is used + * for rapid changes; discrete changes (tab/page) default to `push` so the back + * button undoes them. + * + * The `namespace` identifies this list state for SSR helpers; URL keys use + * the schema field names directly. + * + * @example + * ```tsx + * const [state, setState] = useListState("comments-moderation", { + * tab: { type: "string", default: "pending" }, + * page: { type: "number", default: 1 }, + * }); + * // URL: ?tab=spam&page=3 + * ``` + */ +export function useListState( + namespace: string, + schema: S, +): [InferListState, SetListState] { + const stack = useStackOrNull(); + const router = stack?.router; + const getSearchParams = router?.getSearchParams; + const setSearchParams = router?.setSearchParams; + + const [urlVersion, bumpUrlVersion] = useState(0); + + useEffect(() => { + if (typeof window === "undefined") return; + const onPopState = () => bumpUrlVersion((v) => v + 1); + window.addEventListener("popstate", onPopState); + return () => window.removeEventListener("popstate", onPopState); + }, []); + + const searchParams = useMemo(() => { + void urlVersion; + return getSearchParams?.() ?? new URLSearchParams(); + }, [getSearchParams, urlVersion]); + + const state = useMemo( + () => parseListStateFromSearchParams(namespace, schema, searchParams), + [namespace, schema, searchParams], + ); + + const setState = useCallback>( + (updates, options) => { + const patch = typeof updates === "function" ? updates(state) : updates; + if (!patch || Object.keys(patch).length === 0) return; + + const nextState = { ...state, ...patch }; + const replace = resolveListStateHistoryMode( + schema, + patch, + options?.replace, + ); + + if (setSearchParams && getSearchParams) { + const current = getSearchParams(); + const nextParams = serializeListStateToSearchParams( + namespace, + schema, + nextState, + current, + ); + setSearchParams(nextParams, { replace }); + bumpUrlVersion((v) => v + 1); + } + }, + [namespace, schema, state, setSearchParams, getSearchParams], + ); + + return [state, setState]; +} diff --git a/packages/stack/src/client/index.ts b/packages/stack/src/client/index.ts index bec01889e..ab078a0db 100644 --- a/packages/stack/src/client/index.ts +++ b/packages/stack/src/client/index.ts @@ -114,3 +114,15 @@ export { sitemapEntryToXmlString } from "./sitemap-utils"; export { metaElementsToObject } from "./meta-utils"; export { normalizePath } from "./path-utils"; + +export { + useListState, + parseListStateFromSearchParams, + serializeListStateToSearchParams, + listStateParamKey, + type InferListState, + type ListStateField, + type ListStateSchema, + type SetListState, + type SetListStateOptions, +} from "./hooks/use-list-state"; diff --git a/packages/stack/src/context/i18n.tsx b/packages/stack/src/context/i18n.tsx new file mode 100644 index 000000000..cfddb5a88 --- /dev/null +++ b/packages/stack/src/context/i18n.tsx @@ -0,0 +1,57 @@ +"use client"; +import { createContext, useCallback, useContext, useMemo } from "react"; +import { interpolate } from "../shared/interpolate"; +import type { StackI18nProvider } from "../shared/i18n-types"; + +const I18nContext = createContext(null); + +export function StackI18nBoundary({ + i18n, + children, +}: { + i18n?: StackI18nProvider; + children?: React.ReactNode; +}) { + return ( + {children} + ); +} + +export type TranslateFn = ( + key: string, + defaultValue: string, + params?: Record, +) => string; + +/** + * Returns a `t(key, defaultValue, params?)` function for translatable UI strings. + * + * Without an `i18n` provider on `StackProvider`, returns the default English + * string with `{{param}}` interpolation — identical to hardcoded strings today. + * + * @example + * ```tsx + * const t = useTranslate(); + * return ; + * ``` + */ +export function useTranslate(): TranslateFn { + const i18n = useContext(I18nContext); + + const fallback = useCallback( + (key, defaultValue, params) => interpolate(defaultValue, params), + [], + ); + + return useMemo(() => { + if (!i18n) return fallback; + + return (key, defaultValue, params) => + i18n.translate(key, defaultValue, params); + }, [i18n, fallback]); +} + +/** @internal Access the raw i18n provider (or `null` when none is set). */ +export function useI18nContext(): StackI18nProvider | null { + return useContext(I18nContext); +} diff --git a/packages/stack/src/context/index.ts b/packages/stack/src/context/index.ts index 6687d7309..f67ddb70c 100644 --- a/packages/stack/src/context/index.ts +++ b/packages/stack/src/context/index.ts @@ -1,9 +1,16 @@ export * from "./provider"; export * from "./router"; export { CanAccess, useCan, useIdentity } from "./auth"; +export { useNotify, defaultNotifyProvider } from "./notify"; +export { useTranslate, type TranslateFn } from "./i18n"; export type { CanParams, StackAuthProvider, StackIdentity, StackServerAuthProvider, } from "../shared/auth-types"; +export type { + StackNotifyProvider, + NotifyOptions, +} from "../shared/notify-types"; +export type { StackI18nProvider } from "../shared/i18n-types"; diff --git a/packages/stack/src/context/notify.tsx b/packages/stack/src/context/notify.tsx new file mode 100644 index 000000000..280fbf590 --- /dev/null +++ b/packages/stack/src/context/notify.tsx @@ -0,0 +1,66 @@ +"use client"; +import { createContext, useContext, useMemo } from "react"; +import { toast } from "sonner"; +import type { + NotifyOptions, + StackNotifyProvider, +} from "../shared/notify-types"; + +const NotifyContext = createContext | null>(null); + +/** Sonner-backed defaults used when no custom `notify` prop is configured. */ +export const defaultNotifyProvider: Required = { + success: (message: string, options?: NotifyOptions) => { + toast.success(message, options); + }, + error: (message: string, options?: NotifyOptions) => { + toast.error(message, options); + }, + info: (message: string, options?: NotifyOptions) => { + toast.info(message, options); + }, + warning: (message: string, options?: NotifyOptions) => { + toast.warning(message, options); + }, +}; + +function mergeNotifyProvider( + custom: StackNotifyProvider | undefined, +): Required { + return { + success: custom?.success ?? defaultNotifyProvider.success, + error: custom?.error ?? defaultNotifyProvider.error, + info: custom?.info ?? defaultNotifyProvider.info, + warning: custom?.warning ?? defaultNotifyProvider.warning, + }; +} + +export function StackNotifyBoundary({ + notify, + children, +}: { + notify?: StackNotifyProvider; + children?: React.ReactNode; +}) { + const value = useMemo(() => mergeNotifyProvider(notify), [notify]); + + return ( + {children} + ); +} + +/** + * Returns notification methods routed through the `notify` provider on + * `StackProvider`, falling back to sonner toasts when no override is set. + * + * @example + * ```tsx + * const notify = useNotify(); + * notify.success("Post saved"); + * notify.error("Failed to delete", { description: "Try again later" }); + * ``` + */ +export function useNotify(): Required { + const notify = useContext(NotifyContext); + return notify ?? defaultNotifyProvider; +} diff --git a/packages/stack/src/context/provider.tsx b/packages/stack/src/context/provider.tsx index f88574a8e..a48e81baa 100644 --- a/packages/stack/src/context/provider.tsx +++ b/packages/stack/src/context/provider.tsx @@ -1,7 +1,11 @@ "use client"; import { createContext, useContext, type ReactNode } from "react"; import type { StackAuthProvider } from "../shared/auth-types"; +import type { StackI18nProvider } from "../shared/i18n-types"; +import type { StackNotifyProvider } from "../shared/notify-types"; import { StackAuthBoundary } from "./auth"; +import { StackI18nBoundary } from "./i18n"; +import { StackNotifyBoundary } from "./notify"; import type { StackApiConfig, StackRouter, @@ -155,6 +159,8 @@ export function StackProvider< router, api, auth, + notify, + i18n, }: { children?: ReactNode; overrides?: StackProviderOverrides; @@ -168,6 +174,16 @@ export function StackProvider< * checks pass. */ auth?: StackAuthProvider; + /** + * Optional notification provider. When omitted, sonner toasts are used via + * `useNotify()`. + */ + notify?: StackNotifyProvider; + /** + * Optional i18n provider. When omitted, `useTranslate()` returns English + * defaults with `{{param}}` interpolation. + */ + i18n?: StackI18nProvider; }) { const staticRouter = resolveStaticRouter(router); const value: Omit, "router"> = { @@ -182,23 +198,25 @@ export function StackProvider< children ); - if (router?.useRouter) { - return ( - - {content} - - ); - } - - return ( + const stackTree = router?.useRouter ? ( + + {content} + + ) : ( {content} ); + + return ( + + {stackTree} + + ); } /** diff --git a/packages/stack/src/shared/i18n-types.ts b/packages/stack/src/shared/i18n-types.ts new file mode 100644 index 000000000..a99a774d6 --- /dev/null +++ b/packages/stack/src/shared/i18n-types.ts @@ -0,0 +1,31 @@ +/** + * Optional i18n provider, passed to `StackProvider` via the `i18n` prop. + * + * Without a provider, `useTranslate()` returns the default English string with + * `{{param}}` interpolation — zero setup cost for English-only apps. + * + * @example + * ```tsx + * const i18n: StackI18nProvider = { + * translate: (key, defaultValue, params) => + * t(key, { defaultValue, ...params }), + * }; + * + * + * ``` + */ +export interface StackI18nProvider { + /** + * Translate a namespaced key. Receives the English default and optional + * interpolation params when no translation exists for the key. + */ + translate: ( + key: string, + defaultValue: string, + params?: Record, + ) => string; + /** Return the active locale (optional) */ + getLocale?: () => string; + /** Switch locale (optional) */ + changeLocale?: (locale: string) => void; +} diff --git a/packages/stack/src/shared/interpolate.ts b/packages/stack/src/shared/interpolate.ts new file mode 100644 index 000000000..7506bfff2 --- /dev/null +++ b/packages/stack/src/shared/interpolate.ts @@ -0,0 +1,14 @@ +/** + * Replace `{{key}}` placeholders in a template with stringified param values. + */ +export function interpolate( + template: string, + params?: Record, +): string { + if (!params) return template; + + return template.replace(/\{\{(\w+)\}\}/g, (_, key: string) => { + const value = params[key]; + return value == null ? "" : String(value); + }); +} diff --git a/packages/stack/src/shared/list-state.ts b/packages/stack/src/shared/list-state.ts new file mode 100644 index 000000000..e1db003a0 --- /dev/null +++ b/packages/stack/src/shared/list-state.ts @@ -0,0 +1,147 @@ +import { interpolate } from "./interpolate"; + +export type ListStateFieldType = "string" | "number" | "boolean"; + +export interface ListStateField< + T extends ListStateFieldType = ListStateFieldType, +> { + type: T; + default: T extends "string" ? string : T extends "number" ? number : boolean; + /** + * How URL history is updated when this field changes. + * - `"push"` (default): discrete changes (tab switch, page change) — back button undoes. + * - `"replace"`: rapid changes (typing in a filter) — no extra history entries. + */ + history?: "push" | "replace"; +} + +export type ListStateSchema = Record; + +export type InferListStateValue = + F["type"] extends "string" + ? string + : F["type"] extends "number" + ? number + : boolean; + +export type InferListState = { + [K in keyof S]: InferListStateValue; +}; + +/** + * URL query param key for a list-state field. + * The `namespace` argument identifies the hook instance for SSR helpers but + * does not prefix URL keys — field names are used directly (e.g. `?tab=spam`). + */ +export function listStateParamKey( + _namespace: string, + fieldKey: string, +): string { + return fieldKey; +} + +function parseFieldValue( + field: F, + raw: string | null, +): InferListStateValue { + if (raw == null || raw === "") { + return field.default as InferListStateValue; + } + + switch (field.type) { + case "number": { + const parsed = Number(raw); + return ( + Number.isFinite(parsed) ? parsed : field.default + ) as InferListStateValue; + } + case "boolean": + return (raw === "true") as InferListStateValue; + default: + return raw as InferListStateValue; + } +} + +function serializeFieldValue(value: unknown): string { + return String(value); +} + +function valuesEqual(a: unknown, b: unknown): boolean { + return a === b; +} + +/** + * Parse list state from URL search params (SSR-safe — pass the request URL's + * search string or a `URLSearchParams` instance). + */ +export function parseListStateFromSearchParams( + namespace: string, + schema: S, + searchParams: URLSearchParams | string, +): InferListState { + const params = + typeof searchParams === "string" + ? new URLSearchParams(searchParams) + : searchParams; + + const state = {} as InferListState; + + for (const fieldKey of Object.keys(schema) as Array) { + const field = schema[fieldKey]!; + const paramKey = listStateParamKey(namespace, fieldKey); + state[fieldKey as keyof S] = parseFieldValue( + field, + params.get(paramKey), + ) as InferListState[keyof S]; + } + + return state; +} + +/** + * Serialize list state into URL search params. Default values are omitted for + * clean URLs. + */ +export function serializeListStateToSearchParams( + namespace: string, + schema: S, + state: InferListState, + baseParams?: URLSearchParams, +): URLSearchParams { + const params = new URLSearchParams(baseParams?.toString()); + + for (const fieldKey of Object.keys(schema) as Array) { + const field = schema[fieldKey]!; + const paramKey = listStateParamKey(namespace, fieldKey); + const value = state[fieldKey as keyof S]; + + if (valuesEqual(value, field.default)) { + params.delete(paramKey); + } else { + params.set(paramKey, serializeFieldValue(value)); + } + } + + return params; +} + +/** + * Decide whether a list-state update should use `replace` or `push` history. + */ +export function resolveListStateHistoryMode( + schema: S, + updates: Partial>, + explicit?: boolean, +): boolean { + if (explicit !== undefined) return explicit; + + for (const fieldKey of Object.keys(updates) as Array) { + if (schema[fieldKey]?.history === "replace") { + return true; + } + } + + return false; +} + +export { interpolate }; diff --git a/packages/stack/src/shared/notify-types.ts b/packages/stack/src/shared/notify-types.ts new file mode 100644 index 000000000..dea8ccc0a --- /dev/null +++ b/packages/stack/src/shared/notify-types.ts @@ -0,0 +1,29 @@ +/** + * Options passed to notification methods. + */ +export interface NotifyOptions { + /** Optional longer description shown below the message */ + description?: string; +} + +/** + * Pluggable notification provider, passed to `StackProvider` via the `notify` prop. + * + * When omitted, sonner toasts are used as the default implementation. + * + * @example + * ```tsx + * const notify: StackNotifyProvider = { + * success: (msg) => myToast.success(msg), + * error: (msg) => myToast.error(msg), + * }; + * + * + * ``` + */ +export interface StackNotifyProvider { + success?: (message: string, options?: NotifyOptions) => void; + error?: (message: string, options?: NotifyOptions) => void; + info?: (message: string, options?: NotifyOptions) => void; + warning?: (message: string, options?: NotifyOptions) => void; +} From fc13ffbc901e84a99879903bb6955247cbdb25f2 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 19:31:54 +0000 Subject: [PATCH 015/380] ci: retrigger codegen E2E Co-authored-by: Cursor From 320e7644a80c4956127e5d078c5f9dd6e4f6835a Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 19:52:39 +0000 Subject: [PATCH 016/380] fix: read fresh URL state in useListState setState Address Bugbot feedback: derive next state from getSearchParams() instead of a stale render closure, and re-read params each render so router-driven URL changes are not masked by useMemo caching. Co-authored-by: Cursor --- .../src/__tests__/use-list-state.test.tsx | 20 +++++++ .../stack/src/client/hooks/use-list-state.ts | 52 +++++++++++-------- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/packages/stack/src/__tests__/use-list-state.test.tsx b/packages/stack/src/__tests__/use-list-state.test.tsx index ecc423579..49af1271b 100644 --- a/packages/stack/src/__tests__/use-list-state.test.tsx +++ b/packages/stack/src/__tests__/use-list-state.test.tsx @@ -114,4 +114,24 @@ describe("useListState", () => { { replace: false }, ); }); + + it("preserves prior URL fields when batched in one event", async () => { + let captured: any; + const router = await renderHook((value) => { + captured = value; + }); + + await act(async () => { + captured.setState({ tab: "spam" }); + captured.setState({ page: 3 }); + }); + + const lastCall = + router.setSearchParams.mock.calls[ + router.setSearchParams.mock.calls.length - 1 + ]; + expect(lastCall).toBeDefined(); + const [nextParams] = lastCall!; + expect(nextParams.toString()).toBe("tab=spam&page=3"); + }); }); diff --git a/packages/stack/src/client/hooks/use-list-state.ts b/packages/stack/src/client/hooks/use-list-state.ts index 1aa5c1597..dec73f8ad 100644 --- a/packages/stack/src/client/hooks/use-list-state.ts +++ b/packages/stack/src/client/hooks/use-list-state.ts @@ -1,5 +1,5 @@ "use client"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useStackOrNull } from "../../context/provider"; import type { InferListState, ListStateSchema } from "../../shared/list-state"; import { @@ -68,41 +68,47 @@ export function useListState( return () => window.removeEventListener("popstate", onPopState); }, []); - const searchParams = useMemo(() => { - void urlVersion; - return getSearchParams?.() ?? new URLSearchParams(); - }, [getSearchParams, urlVersion]); - - const state = useMemo( - () => parseListStateFromSearchParams(namespace, schema, searchParams), - [namespace, schema, searchParams], + // Read search params on every render so router-driven URL changes are picked + // up even when `getSearchParams` is referentially stable. `urlVersion` covers + // back/forward when the parent does not re-render. + void urlVersion; + const state = parseListStateFromSearchParams( + namespace, + schema, + getSearchParams?.() ?? new URLSearchParams(), ); const setState = useCallback>( (updates, options) => { - const patch = typeof updates === "function" ? updates(state) : updates; + if (!setSearchParams || !getSearchParams) return; + + const currentParams = getSearchParams(); + const currentState = parseListStateFromSearchParams( + namespace, + schema, + currentParams, + ); + const patch = + typeof updates === "function" ? updates(currentState) : updates; if (!patch || Object.keys(patch).length === 0) return; - const nextState = { ...state, ...patch }; + const nextState = { ...currentState, ...patch }; const replace = resolveListStateHistoryMode( schema, patch, options?.replace, ); - if (setSearchParams && getSearchParams) { - const current = getSearchParams(); - const nextParams = serializeListStateToSearchParams( - namespace, - schema, - nextState, - current, - ); - setSearchParams(nextParams, { replace }); - bumpUrlVersion((v) => v + 1); - } + const nextParams = serializeListStateToSearchParams( + namespace, + schema, + nextState, + currentParams, + ); + setSearchParams(nextParams, { replace }); + bumpUrlVersion((v) => v + 1); }, - [namespace, schema, state, setSearchParams, getSearchParams], + [namespace, schema, setSearchParams, getSearchParams], ); return [state, setState]; From 48602787b652c07a7e35c7b527ff9686f161b531 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 20:16:03 +0000 Subject: [PATCH 017/380] fix: coalesce same-event useListState updates into one history entry Multiple setState calls in one event now merge into a single setSearchParams call via microtask batching, and no-op updates skip history entirely, so one back press undoes one user action. Co-authored-by: Cursor --- .../src/__tests__/use-list-state.test.tsx | 39 ++++++++-- .../stack/src/client/hooks/use-list-state.ts | 75 ++++++++++++++----- 2 files changed, 89 insertions(+), 25 deletions(-) diff --git a/packages/stack/src/__tests__/use-list-state.test.tsx b/packages/stack/src/__tests__/use-list-state.test.tsx index 49af1271b..89066cbce 100644 --- a/packages/stack/src/__tests__/use-list-state.test.tsx +++ b/packages/stack/src/__tests__/use-list-state.test.tsx @@ -115,7 +115,7 @@ describe("useListState", () => { ); }); - it("preserves prior URL fields when batched in one event", async () => { + it("coalesces same-event updates into a single history entry", async () => { let captured: any; const router = await renderHook((value) => { captured = value; @@ -126,12 +126,37 @@ describe("useListState", () => { captured.setState({ page: 3 }); }); - const lastCall = - router.setSearchParams.mock.calls[ - router.setSearchParams.mock.calls.length - 1 - ]; - expect(lastCall).toBeDefined(); - const [nextParams] = lastCall!; + expect(router.setSearchParams).toHaveBeenCalledTimes(1); + const [nextParams] = router.setSearchParams.mock.calls[0]!; expect(nextParams.toString()).toBe("tab=spam&page=3"); }); + + it("merges pending patches into functional updaters within one event", async () => { + let captured: any; + const router = await renderHook((value) => { + captured = value; + }); + + await act(async () => { + captured.setState({ page: 2 }); + captured.setState((prev: any) => ({ page: prev.page + 1 })); + }); + + expect(router.setSearchParams).toHaveBeenCalledTimes(1); + const [nextParams] = router.setSearchParams.mock.calls[0]!; + expect(nextParams.toString()).toBe("page=3"); + }); + + it("does not push history when the update is a no-op", async () => { + let captured: any; + const router = await renderHook((value) => { + captured = value; + }); + + await act(async () => { + captured.setState({ tab: "pending", page: 1 }); + }); + + expect(router.setSearchParams).not.toHaveBeenCalled(); + }); }); diff --git a/packages/stack/src/client/hooks/use-list-state.ts b/packages/stack/src/client/hooks/use-list-state.ts index dec73f8ad..90c428e4e 100644 --- a/packages/stack/src/client/hooks/use-list-state.ts +++ b/packages/stack/src/client/hooks/use-list-state.ts @@ -1,5 +1,5 @@ "use client"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { useStackOrNull } from "../../context/provider"; import type { InferListState, ListStateSchema } from "../../shared/list-state"; import { @@ -78,35 +78,74 @@ export function useListState( getSearchParams?.() ?? new URLSearchParams(), ); + // Updates issued in the same tick are coalesced into a single + // `setSearchParams` call. Router bindings commit URL changes + // asynchronously (Next.js `router.push`, React Router `navigate`), so + // consecutive `setState` calls in one event would otherwise read a stale + // URL and drop earlier patches — and each call would add its own history + // entry, breaking one-back-press-undoes-one-action semantics. + const pendingRef = useRef<{ + patch: Partial>; + explicitReplace: boolean | undefined; + } | null>(null); + const setState = useCallback>( (updates, options) => { if (!setSearchParams || !getSearchParams) return; - const currentParams = getSearchParams(); const currentState = parseListStateFromSearchParams( namespace, schema, - currentParams, + getSearchParams(), ); + const pending = pendingRef.current; + const baseState = pending + ? { ...currentState, ...pending.patch } + : currentState; const patch = - typeof updates === "function" ? updates(currentState) : updates; + typeof updates === "function" ? updates(baseState) : updates; if (!patch || Object.keys(patch).length === 0) return; - const nextState = { ...currentState, ...patch }; - const replace = resolveListStateHistoryMode( - schema, - patch, - options?.replace, - ); + if (pending) { + pending.patch = { ...pending.patch, ...patch }; + if (options?.replace !== undefined) { + pending.explicitReplace = options.replace; + } + return; + } - const nextParams = serializeListStateToSearchParams( - namespace, - schema, - nextState, - currentParams, - ); - setSearchParams(nextParams, { replace }); - bumpUrlVersion((v) => v + 1); + pendingRef.current = { + patch: { ...patch }, + explicitReplace: options?.replace, + }; + + queueMicrotask(() => { + const flushed = pendingRef.current; + pendingRef.current = null; + if (!flushed) return; + + const currentParams = getSearchParams(); + const nextState = { + ...parseListStateFromSearchParams(namespace, schema, currentParams), + ...flushed.patch, + }; + const replace = resolveListStateHistoryMode( + schema, + flushed.patch, + flushed.explicitReplace, + ); + const nextParams = serializeListStateToSearchParams( + namespace, + schema, + nextState, + currentParams, + ); + // No-op updates (merged state already matches the URL) must not + // create history entries. + if (nextParams.toString() === currentParams.toString()) return; + setSearchParams(nextParams, { replace }); + bumpUrlVersion((v) => v + 1); + }); }, [namespace, schema, setSearchParams, getSearchParams], ); From ffe99409146111a9b695a5b1e29bf7e3ee1f03ec Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 20:30:38 +0000 Subject: [PATCH 018/380] fix: batch same-tick useListState updates across hook instances Move the coalescing queue to module scope so concurrent updates from different useListState instances merge into one setSearchParams call instead of the later flush clobbering the earlier one's params. Co-authored-by: Cursor --- .../src/__tests__/use-list-state.test.tsx | 39 +++++ .../stack/src/client/hooks/use-list-state.ts | 147 +++++++++++------- 2 files changed, 132 insertions(+), 54 deletions(-) diff --git a/packages/stack/src/__tests__/use-list-state.test.tsx b/packages/stack/src/__tests__/use-list-state.test.tsx index 89066cbce..0519685cb 100644 --- a/packages/stack/src/__tests__/use-list-state.test.tsx +++ b/packages/stack/src/__tests__/use-list-state.test.tsx @@ -147,6 +147,45 @@ describe("useListState", () => { expect(nextParams.toString()).toBe("page=3"); }); + it("coalesces updates from multiple hook instances in one event", async () => { + const router = createMockRouter(); + let capturedA: any; + let capturedB: any; + + function ProbeA() { + const [state, setState] = useListState("comments-moderation", schema); + capturedA = { state, setState }; + return null; + } + function ProbeB() { + const [state, setState] = useListState("media-library", { + folder: { type: "string" as const, default: "root" }, + }); + capturedB = { state, setState }; + return null; + } + + await act(async () => { + root.render( + + + + , + ); + }); + + await act(async () => { + capturedA.setState({ tab: "spam" }); + capturedB.setState({ folder: "photos" }); + }); + + expect(router.setSearchParams).toHaveBeenCalledTimes(1); + const [nextParams] = router.setSearchParams.mock.calls[0]!; + expect(nextParams.toString()).toBe("tab=spam&folder=photos"); + expect(capturedA.state).toEqual({ tab: "spam", page: 1, filter: "" }); + expect(capturedB.state).toEqual({ folder: "photos" }); + }); + it("does not push history when the update is a no-op", async () => { let captured: any; const router = await renderHook((value) => { diff --git a/packages/stack/src/client/hooks/use-list-state.ts b/packages/stack/src/client/hooks/use-list-state.ts index 90c428e4e..400689a66 100644 --- a/packages/stack/src/client/hooks/use-list-state.ts +++ b/packages/stack/src/client/hooks/use-list-state.ts @@ -1,5 +1,5 @@ "use client"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useStackOrNull } from "../../context/provider"; import type { InferListState, ListStateSchema } from "../../shared/list-state"; import { @@ -31,6 +31,82 @@ export type SetListState = ( options?: SetListStateOptions, ) => void; +interface PendingUpdate { + namespace: string; + schema: ListStateSchema; + patch: Record; + explicitReplace: boolean | undefined; + getSearchParams: () => URLSearchParams; + setSearchParams: ( + next: URLSearchParams, + opts?: { replace?: boolean }, + ) => void; + onFlushed: () => void; +} + +// Same-tick updates from ALL useListState instances are coalesced into one +// `setSearchParams` call. Router bindings commit URL changes asynchronously +// (Next.js `router.push`, React Router `navigate`), so a second update in the +// same event would read a stale URL and drop what the first one wrote — and +// each call would add its own history entry, breaking +// one-back-press-undoes-one-action semantics. The URL is a per-window +// singleton, so a module-level queue is the correct batching scope; `setState` +// only runs in client event handlers, never during SSR. +let pendingQueue: PendingUpdate[] | null = null; + +function enqueueListStateUpdate(update: PendingUpdate): void { + if (pendingQueue) { + pendingQueue.push(update); + return; + } + + pendingQueue = [update]; + queueMicrotask(() => { + const queue = pendingQueue; + pendingQueue = null; + if (!queue || queue.length === 0) return; + + const first = queue[0]!; + const currentParams = first.getSearchParams(); + + let params = currentParams; + let replace = true; + for (const entry of queue) { + const nextState = { + ...parseListStateFromSearchParams( + entry.namespace, + entry.schema, + params, + ), + ...entry.patch, + }; + params = serializeListStateToSearchParams( + entry.namespace, + entry.schema, + nextState as InferListState, + params, + ); + // A single push among the batched updates makes the whole flush a + // push, so the combined action stays one back-press away. + if ( + !resolveListStateHistoryMode( + entry.schema, + entry.patch as Partial>, + entry.explicitReplace, + ) + ) { + replace = false; + } + } + + // No-op updates (merged state already matches the URL) must not create + // history entries. + if (params.toString() === currentParams.toString()) return; + first.setSearchParams(params, { replace }); + for (const entry of queue) entry.onFlushed(); + }); +} + /** * Sync list UI state (filters, tabs, pagination) with URL search params. * @@ -78,73 +154,36 @@ export function useListState( getSearchParams?.() ?? new URLSearchParams(), ); - // Updates issued in the same tick are coalesced into a single - // `setSearchParams` call. Router bindings commit URL changes - // asynchronously (Next.js `router.push`, React Router `navigate`), so - // consecutive `setState` calls in one event would otherwise read a stale - // URL and drop earlier patches — and each call would add its own history - // entry, breaking one-back-press-undoes-one-action semantics. - const pendingRef = useRef<{ - patch: Partial>; - explicitReplace: boolean | undefined; - } | null>(null); - const setState = useCallback>( (updates, options) => { if (!setSearchParams || !getSearchParams) return; - const currentState = parseListStateFromSearchParams( + // Base state = URL state + patches already queued this tick, so + // functional updaters see the values earlier calls just set. + let baseState = parseListStateFromSearchParams( namespace, schema, getSearchParams(), ); - const pending = pendingRef.current; - const baseState = pending - ? { ...currentState, ...pending.patch } - : currentState; + if (pendingQueue) { + for (const entry of pendingQueue) { + if (entry.namespace === namespace) { + baseState = { ...baseState, ...entry.patch }; + } + } + } const patch = typeof updates === "function" ? updates(baseState) : updates; if (!patch || Object.keys(patch).length === 0) return; - if (pending) { - pending.patch = { ...pending.patch, ...patch }; - if (options?.replace !== undefined) { - pending.explicitReplace = options.replace; - } - return; - } - - pendingRef.current = { + enqueueListStateUpdate({ + namespace, + schema, patch: { ...patch }, explicitReplace: options?.replace, - }; - - queueMicrotask(() => { - const flushed = pendingRef.current; - pendingRef.current = null; - if (!flushed) return; - - const currentParams = getSearchParams(); - const nextState = { - ...parseListStateFromSearchParams(namespace, schema, currentParams), - ...flushed.patch, - }; - const replace = resolveListStateHistoryMode( - schema, - flushed.patch, - flushed.explicitReplace, - ); - const nextParams = serializeListStateToSearchParams( - namespace, - schema, - nextState, - currentParams, - ); - // No-op updates (merged state already matches the URL) must not - // create history entries. - if (nextParams.toString() === currentParams.toString()) return; - setSearchParams(nextParams, { replace }); - bumpUrlVersion((v) => v + 1); + getSearchParams, + setSearchParams, + onFlushed: () => bumpUrlVersion((v) => v + 1), }); }, [namespace, schema, setSearchParams, getSearchParams], From 75a1785b40e2d0623d774e676100c76b52b4e117 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 20:42:20 +0000 Subject: [PATCH 019/380] fix: compare parsed state, not param strings, for useListState no-op detection URLSearchParams serialization is order-sensitive and the URL may hold explicit default values, so string equality could miss state-identical updates and push spurious history entries. Compare parsed field values per entry instead, and use order-insensitive comparison for the final net-no-op check. Part of #133 Co-authored-by: Cursor --- .../src/__tests__/use-list-state.test.tsx | 31 +++++++++++++- .../stack/src/client/hooks/use-list-state.ts | 42 ++++++++++++++----- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/packages/stack/src/__tests__/use-list-state.test.tsx b/packages/stack/src/__tests__/use-list-state.test.tsx index 0519685cb..fdf818599 100644 --- a/packages/stack/src/__tests__/use-list-state.test.tsx +++ b/packages/stack/src/__tests__/use-list-state.test.tsx @@ -13,8 +13,8 @@ const schema = { filter: { type: "string" as const, default: "", history: "replace" as const }, }; -function createMockRouter() { - let params = new URLSearchParams(); +function createMockRouter(initial = "") { + let params = new URLSearchParams(initial); const setSearchParams = vi.fn( (next: URLSearchParams, opts?: { replace?: boolean }) => { params = new URLSearchParams(next.toString()); @@ -186,6 +186,33 @@ describe("useListState", () => { expect(capturedB.state).toEqual({ folder: "photos" }); }); + it("treats state-identical updates as no-ops even when the URL holds explicit defaults", async () => { + // URL contains `tab=pending` (the default) explicitly: serializing the + // same state would drop it and change the query string without changing + // state. That must not create a history entry. + const router = createMockRouter("tab=pending&page=3"); + let captured: any; + + function Probe() { + const [state, setState] = useListState("comments-moderation", schema); + captured = { state, setState }; + return null; + } + await act(async () => { + root.render( + + + , + ); + }); + + await act(async () => { + captured.setState({ page: 3 }); + }); + + expect(router.setSearchParams).not.toHaveBeenCalled(); + }); + it("does not push history when the update is a no-op", async () => { let captured: any; const router = await renderHook((value) => { diff --git a/packages/stack/src/client/hooks/use-list-state.ts b/packages/stack/src/client/hooks/use-list-state.ts index 400689a66..c867901a4 100644 --- a/packages/stack/src/client/hooks/use-list-state.ts +++ b/packages/stack/src/client/hooks/use-list-state.ts @@ -54,6 +54,15 @@ interface PendingUpdate { // only runs in client event handlers, never during SSR. let pendingQueue: PendingUpdate[] | null = null; +function searchParamsEqual(a: URLSearchParams, b: URLSearchParams): boolean { + const sorted = (params: URLSearchParams) => { + const copy = new URLSearchParams(params.toString()); + copy.sort(); + return copy.toString(); + }; + return sorted(a) === sorted(b); +} + function enqueueListStateUpdate(update: PendingUpdate): void { if (pendingQueue) { pendingQueue.push(update); @@ -71,15 +80,25 @@ function enqueueListStateUpdate(update: PendingUpdate): void { let params = currentParams; let replace = true; + let changed = false; for (const entry of queue) { - const nextState = { - ...parseListStateFromSearchParams( - entry.namespace, - entry.schema, - params, - ), - ...entry.patch, - }; + const prevState = parseListStateFromSearchParams( + entry.namespace, + entry.schema, + params, + ); + const nextState = { ...prevState, ...entry.patch }; + // Compare parsed states, not serialized strings: the URL may hold an + // explicit default (`?tab=pending`) or differ only in param order, and + // such state-identical updates must not touch history. + if ( + Object.keys(entry.schema).every((key) => + Object.is(prevState[key], nextState[key]), + ) + ) { + continue; + } + changed = true; params = serializeListStateToSearchParams( entry.namespace, entry.schema, @@ -99,9 +118,10 @@ function enqueueListStateUpdate(update: PendingUpdate): void { } } - // No-op updates (merged state already matches the URL) must not create - // history entries. - if (params.toString() === currentParams.toString()) return; + // Also skip when later entries reverted earlier ones back to the URL's + // current state (net no-op across the batch). Compare order-insensitively: + // serialization may reorder params without changing state. + if (!changed || searchParamsEqual(params, currentParams)) return; first.setSearchParams(params, { replace }); for (const entry of queue) entry.onFlushed(); }); From 12a23138867ab04b5019eed4b08fe2e98be96434 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 20:48:55 +0000 Subject: [PATCH 020/380] fix: notify all useListState instances when any instance flushes a URL update Replace per-instance urlVersion state with a module-level store consumed via useSyncExternalStore. Previously only the instances that called setState re-rendered after a flush, so sibling hooks sharing query keys could serve stale state when the router binding does not re-render on search-param changes (e.g. the Next.js preset). The popstate listener moves into the shared store subscription. Part of #133 Co-authored-by: Cursor --- .../src/__tests__/use-list-state.test.tsx | 35 ++++++++++++ .../stack/src/client/hooks/use-list-state.ts | 57 +++++++++++++------ 2 files changed, 75 insertions(+), 17 deletions(-) diff --git a/packages/stack/src/__tests__/use-list-state.test.tsx b/packages/stack/src/__tests__/use-list-state.test.tsx index fdf818599..cf905d511 100644 --- a/packages/stack/src/__tests__/use-list-state.test.tsx +++ b/packages/stack/src/__tests__/use-list-state.test.tsx @@ -186,6 +186,41 @@ describe("useListState", () => { expect(capturedB.state).toEqual({ folder: "photos" }); }); + it("re-renders sibling instances that did not call setState", async () => { + const router = createMockRouter(); + let capturedA: any; + let capturedB: any; + + // Two instances of the same list state (e.g. a toolbar and a pager + // rendered in separate subtrees). Only A updates; B must still see it. + function ProbeA() { + const [state, setState] = useListState("comments-moderation", schema); + capturedA = { state, setState }; + return null; + } + function ProbeB() { + const [state] = useListState("comments-moderation", schema); + capturedB = { state }; + return null; + } + + await act(async () => { + root.render( + + + + , + ); + }); + + await act(async () => { + capturedA.setState({ page: 5 }); + }); + + expect(capturedA.state.page).toBe(5); + expect(capturedB.state.page).toBe(5); + }); + it("treats state-identical updates as no-ops even when the URL holds explicit defaults", async () => { // URL contains `tab=pending` (the default) explicitly: serializing the // same state would drop it and change the query string without changing diff --git a/packages/stack/src/client/hooks/use-list-state.ts b/packages/stack/src/client/hooks/use-list-state.ts index c867901a4..b180844ac 100644 --- a/packages/stack/src/client/hooks/use-list-state.ts +++ b/packages/stack/src/client/hooks/use-list-state.ts @@ -1,5 +1,5 @@ "use client"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useSyncExternalStore } from "react"; import { useStackOrNull } from "../../context/provider"; import type { InferListState, ListStateSchema } from "../../shared/list-state"; import { @@ -41,7 +41,39 @@ interface PendingUpdate { next: URLSearchParams, opts?: { replace?: boolean }, ) => void; - onFlushed: () => void; +} + +// All useListState instances subscribe to this module-level store so that a +// URL flush triggered by ANY instance re-renders every mounted hook — not just +// the ones that called setState. Needed because router bindings may commit URL +// changes without re-rendering unrelated subtrees (e.g. the Next.js preset). +let urlVersion = 0; +const urlListeners = new Set<() => void>(); + +function notifyUrlChanged(): void { + urlVersion++; + for (const listener of urlListeners) listener(); +} + +function subscribeToUrl(listener: () => void): () => void { + if (urlListeners.size === 0 && typeof window !== "undefined") { + window.addEventListener("popstate", notifyUrlChanged); + } + urlListeners.add(listener); + return () => { + urlListeners.delete(listener); + if (urlListeners.size === 0 && typeof window !== "undefined") { + window.removeEventListener("popstate", notifyUrlChanged); + } + }; +} + +function getUrlVersion(): number { + return urlVersion; +} + +function getServerUrlVersion(): number { + return 0; } // Same-tick updates from ALL useListState instances are coalesced into one @@ -123,7 +155,7 @@ function enqueueListStateUpdate(update: PendingUpdate): void { // serialization may reorder params without changing state. if (!changed || searchParamsEqual(params, currentParams)) return; first.setSearchParams(params, { replace }); - for (const entry of queue) entry.onFlushed(); + notifyUrlChanged(); }); } @@ -155,19 +187,11 @@ export function useListState( const getSearchParams = router?.getSearchParams; const setSearchParams = router?.setSearchParams; - const [urlVersion, bumpUrlVersion] = useState(0); - - useEffect(() => { - if (typeof window === "undefined") return; - const onPopState = () => bumpUrlVersion((v) => v + 1); - window.addEventListener("popstate", onPopState); - return () => window.removeEventListener("popstate", onPopState); - }, []); - - // Read search params on every render so router-driven URL changes are picked - // up even when `getSearchParams` is referentially stable. `urlVersion` covers - // back/forward when the parent does not re-render. - void urlVersion; + // Every instance re-renders whenever any instance flushes a URL update (or + // on back/forward), so siblings sharing query keys never serve stale state. + // Search params are re-read on every render, so router-driven changes are + // also picked up even when `getSearchParams` is referentially stable. + useSyncExternalStore(subscribeToUrl, getUrlVersion, getServerUrlVersion); const state = parseListStateFromSearchParams( namespace, schema, @@ -203,7 +227,6 @@ export function useListState( explicitReplace: options?.replace, getSearchParams, setSearchParams, - onFlushed: () => bumpUrlVersion((v) => v + 1), }); }, [namespace, schema, setSearchParams, getSearchParams], From 68b73a3da9fca4c1c5b99349a5beac23380cef35 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 21:45:09 +0000 Subject: [PATCH 021/380] feat: add createResource factory and migrate blog plugin onto it Adds a core resource-hook factory to @btst/stack/plugins/client: - StackError contract + shared isErrorResponse/toError/SHARED_QUERY_CONFIG, with toError mapping better-call Zod issues to field-level errors - createEndpoint wrapper that preserves Zod validation issues in 400 bodies - server-safe resource declarations + createResourceQueryKeys (SSR/SSG-safe query-key factory with @lukemorales-compatible shapes) - createResource hooks layer (plain/suspense/infinite queries, mutations with declarative invalidation, cache seeding and refresh) via the new client-only @btst/stack/plugins/client/hooks entry - per-resource useForm (create/edit lifecycle, notify, redirect, field errors) and useSelect (debounced search + current-value preload) Migrates the blog plugin as the reference consumer: query-keys.ts and blog-hooks.tsx are thin wrappers over the factory (public hook APIs unchanged, query keys byte-identical), post-forms.tsx drives submit/toast/ field errors through useForm. Part of #129 Co-authored-by: Cursor --- .../skills/btst-client-plugin-dev/SKILL.md | 69 ++- packages/stack/build.config.ts | 1 + packages/stack/package.json | 13 + .../src/__tests__/blog-query-keys.test.ts | 86 ++++ .../src/__tests__/resource-factory.test.tsx | 461 ++++++++++++++++++ .../src/__tests__/resource-use-form.test.tsx | 352 +++++++++++++ .../__tests__/resource-use-select.test.tsx | 248 ++++++++++ .../stack/src/__tests__/stack-error.test.ts | 174 +++++++ .../stack/src/plugins/api/create-endpoint.ts | 82 ++++ packages/stack/src/plugins/api/index.ts | 5 +- .../client/components/forms/post-forms.tsx | 192 +++++--- .../plugins/blog/client/hooks/blog-hooks.tsx | 387 ++------------- .../blog/client/hooks/blog-resource.ts | 13 + .../blog/client/localization/blog-forms.ts | 1 + packages/stack/src/plugins/blog/query-keys.ts | 405 ++++++--------- .../stack/src/plugins/client/hooks/index.tsx | 32 ++ packages/stack/src/plugins/client/index.ts | 30 ++ .../src/plugins/client/resource/errors.ts | 160 ++++++ .../src/plugins/client/resource/hooks.tsx | 282 +++++++++++ .../src/plugins/client/resource/internal.ts | 110 +++++ .../src/plugins/client/resource/queries.ts | 279 +++++++++++ .../plugins/client/resource/use-debounce.ts | 18 + .../src/plugins/client/resource/use-form.ts | 236 +++++++++ .../src/plugins/client/resource/use-select.ts | 196 ++++++++ 24 files changed, 3149 insertions(+), 683 deletions(-) create mode 100644 packages/stack/src/__tests__/blog-query-keys.test.ts create mode 100644 packages/stack/src/__tests__/resource-factory.test.tsx create mode 100644 packages/stack/src/__tests__/resource-use-form.test.tsx create mode 100644 packages/stack/src/__tests__/resource-use-select.test.tsx create mode 100644 packages/stack/src/__tests__/stack-error.test.ts create mode 100644 packages/stack/src/plugins/api/create-endpoint.ts create mode 100644 packages/stack/src/plugins/blog/client/hooks/blog-resource.ts create mode 100644 packages/stack/src/plugins/client/hooks/index.tsx create mode 100644 packages/stack/src/plugins/client/resource/errors.ts create mode 100644 packages/stack/src/plugins/client/resource/hooks.tsx create mode 100644 packages/stack/src/plugins/client/resource/internal.ts create mode 100644 packages/stack/src/plugins/client/resource/queries.ts create mode 100644 packages/stack/src/plugins/client/resource/use-debounce.ts create mode 100644 packages/stack/src/plugins/client/resource/use-form.ts create mode 100644 packages/stack/src/plugins/client/resource/use-select.ts diff --git a/.agents/skills/btst-client-plugin-dev/SKILL.md b/.agents/skills/btst-client-plugin-dev/SKILL.md index 5e10f7b99..e5ed85d79 100644 --- a/.agents/skills/btst-client-plugin-dev/SKILL.md +++ b/.agents/skills/btst-client-plugin-dev/SKILL.md @@ -16,9 +16,73 @@ src/plugins/{name}/ pages/ my-page.tsx ← wrapper: ComposedRoute + lazy import my-page.internal.tsx ← actual UI: useSuspenseQuery - query-keys.ts ← React Query key factory + query-keys.ts ← resource declaration + query key factory ``` +## Data hooks: use `createResource` (new plugins) + +Don't hand-write the useQuery/useMutation + `isErrorResponse`/`toError` plumbing. +Declare the plugin's resources once in `query-keys.ts` and generate everything: + +```typescript +// query-keys.ts (server-safe — no React) +import { createResourceQueryKeys, type ResourcesDeclaration } from "@btst/stack/plugins/client"; + +export const myResources = { + posts: { + queries: { + list: { path: "/posts", query: (p?: ListParams) => ({...}), key: (p?: ListParams) => [discriminator(p)], + select: (d: any): Item[] => d?.items ?? [], infinite: true, pageSize: (p?: ListParams) => p?.limit ?? 10 }, + detail: { path: "/posts", query: (slug: string) => ({ slug, limit: 1 }), key: (slug: string) => [slug], + select: (d: any): Item | null => d?.items?.[0] ?? null, skip: (slug: string) => !slug }, + }, + mutations: { + create: { path: "@post/posts", method: "POST", input: (vars: CreateInput) => ({ body: vars }), + select: (d: any) => d as Item | null, invalidates: ["posts.list"], + setData: { query: "detail", args: (r: Item | null) => (r?.slug ? [r.slug] : null) } }, + }, + }, +} satisfies ResourcesDeclaration; + +// SSR loaders keep using the same factory (keys match @lukemorales shapes) +export function createMyQueryKeys(client, headers?: HeadersInit) { + return createResourceQueryKeys(client, myResources, headers); +} +``` + +```typescript +// client/hooks.ts ("use client") +import { createResource } from "@btst/stack/plugins/client/hooks"; +import { myResources } from "../query-keys"; + +const my = createResource({ plugin: "my-plugin", resources: myResources }); + +export const usePosts = (params?: ListParams) => my.posts.list.useInfinite([params]); +export const useSuspensePost = (slug: string) => my.posts.detail.useSuspense([slug]); +export const useCreatePost = () => my.posts.create.use(); +``` + +Generated per query: `use(args, { enabled? })`, `useSuspense(args)` (plain) or +`useInfinite`/`useSuspenseInfinite` (when `infinite: true`). Suspense variants re-throw +refetch errors automatically. Mutations get `use()` with declarative `invalidates` +(`"resource"` or `"resource.query"` prefixes), optional `setData` cache seeding, and an +awaited `refresh()` after invalidation. Errors are normalized to `StackError` +(`statusCode`, field-level `errors` from Zod issues). + +Each resource also exposes: + +- `my.posts.useForm({ action, id?, record?, defaults?, toCreateVars?, toUpdateVars?, successMessage?, errorMessage?, redirect?, onSuccess? })` + — create/edit lifecycle: fetches the record for edit, runs the right mutation, + notifies via `useNotify()`, redirects via the router adapter, and exposes + `fieldErrors` (map server Zod issues onto react-hook-form with `setError`). +- `my.posts.useSelect({ searchArgs, getOptionValue, getOptionLabel, value?, preload? })` + — debounced server-side search + current-value preloading for relation pickers. + +`SHARED_QUERY_CONFIG`, `isErrorResponse`, `toError`, and `StackError` live in +`@btst/stack/plugins/client` — never copy them into a plugin. The blog plugin +(`src/plugins/blog/query-keys.ts`, `client/hooks/blog-hooks.tsx`) is the reference +consumer. + ## Server/client module boundary `client/plugin.tsx` must stay import-safe on the server. Next.js (including SSG build) @@ -32,6 +96,9 @@ Rules: - Keep `client/plugin.tsx` free of React hooks (`useState`, `useEffect`, etc.). - Put hook utilities in a separate client-only module (`client/hooks.ts`) with `"use client"`, and re-export them from `client/index.ts`. +- `createResource` comes from the client-only entry `@btst/stack/plugins/client/hooks`; + the server-safe `@btst/stack/plugins/client` entry only exposes + `createResourceQueryKeys` + declaration types for `query-keys.ts` and loaders. - UI components can remain client components as needed; only the plugin factory entry must stay server-import-safe. diff --git a/packages/stack/build.config.ts b/packages/stack/build.config.ts index 0a5bd1da8..ccb10bcee 100644 --- a/packages/stack/build.config.ts +++ b/packages/stack/build.config.ts @@ -80,6 +80,7 @@ export default defineBuildConfig({ // plugin development entries "./src/plugins/api/index.ts", "./src/plugins/client/index.ts", + "./src/plugins/client/hooks/index.tsx", // blog plugin entries "./src/plugins/blog/api/index.ts", "./src/plugins/blog/client/index.ts", diff --git a/packages/stack/package.json b/packages/stack/package.json index 2dca0c632..7ac43e564 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -137,6 +137,16 @@ "default": "./dist/plugins/client/index.cjs" } }, + "./plugins/client/hooks": { + "import": { + "types": "./dist/plugins/client/hooks/index.d.ts", + "default": "./dist/plugins/client/hooks/index.mjs" + }, + "require": { + "types": "./dist/plugins/client/hooks/index.d.cts", + "default": "./dist/plugins/client/hooks/index.cjs" + } + }, "./plugins/blog/api": { "import": { "types": "./dist/plugins/blog/api/index.d.ts", @@ -650,6 +660,9 @@ "plugins/client": [ "./dist/plugins/client/index.d.ts" ], + "plugins/client/hooks": [ + "./dist/plugins/client/hooks/index.d.ts" + ], "plugins/blog/api": [ "./dist/plugins/blog/api/index.d.ts" ], diff --git a/packages/stack/src/__tests__/blog-query-keys.test.ts b/packages/stack/src/__tests__/blog-query-keys.test.ts new file mode 100644 index 000000000..8960407e6 --- /dev/null +++ b/packages/stack/src/__tests__/blog-query-keys.test.ts @@ -0,0 +1,86 @@ +/** + * SSG guard: the factory-generated blog query keys must stay deep-equal to + * the `BLOG_QUERY_KEYS` builders used by `prefetchForRoute` (DB path). + * Key drift breaks React Query cache hydration silently during `next build`. + */ +import { describe, expect, it, vi } from "vitest"; +import { BLOG_QUERY_KEYS } from "../plugins/blog/api/query-key-defs"; +import { createBlogQueryKeys } from "../plugins/blog/query-keys"; + +const client = vi.fn() as any; + +describe("blog query keys match SSG prefetch keys", () => { + const queries = createBlogQueryKeys(client); + + it("posts list keys match for default params", () => { + expect([...queries.posts.list({ published: true }).queryKey]).toEqual([ + ...BLOG_QUERY_KEYS.postsList({ published: true }), + ]); + }); + + it("posts list keys match for drafts and custom limits", () => { + expect([ + ...queries.posts.list({ published: false, limit: 25 }).queryKey, + ]).toEqual([...BLOG_QUERY_KEYS.postsList({ published: false, limit: 25 })]); + }); + + it("posts list keys match for tag filtering", () => { + expect([ + ...queries.posts.list({ published: true, tagSlug: "news" }).queryKey, + ]).toEqual([ + ...BLOG_QUERY_KEYS.postsList({ published: true, tagSlug: "news" }), + ]); + }); + + it("posts list defaults published to true like the SSR loader", () => { + expect([...queries.posts.list().queryKey]).toEqual([ + ...BLOG_QUERY_KEYS.postsList({ published: true }), + ]); + }); + + it("normalizes a whitespace-only query the same way", () => { + expect([ + ...queries.posts.list({ published: true, query: " " }).queryKey, + ]).toEqual([...BLOG_QUERY_KEYS.postsList({ published: true })]); + }); + + it("post detail keys match", () => { + expect([...queries.posts.detail("my-post").queryKey]).toEqual([ + ...BLOG_QUERY_KEYS.postDetail("my-post"), + ]); + }); + + it("tags list keys match", () => { + expect([...queries.tags.list().queryKey]).toEqual([ + ...BLOG_QUERY_KEYS.tagsList(), + ]); + }); + + it("exposes the same _def prefixes as the previous factory", () => { + expect([...queries.posts._def]).toEqual(["posts"]); + expect([...queries.posts.list._def]).toEqual(["posts", "list"]); + expect([...queries.drafts.list._def]).toEqual(["drafts", "list"]); + }); + + it("drafts list keys keep the legacy limit-only shape", () => { + expect([...queries.drafts.list({ limit: 10 }).queryKey]).toEqual([ + "drafts", + "list", + { limit: 10 }, + ]); + expect([...queries.drafts.list().queryKey]).toEqual(["drafts", "list", {}]); + }); + + it("bespoke nextPrevious and recent keys keep their legacy shapes", () => { + const date = new Date("2024-01-01T00:00:00.000Z"); + expect([...queries.posts.nextPrevious(date).queryKey]).toEqual([ + "posts", + "nextPrevious", + "nextPrevious", + date, + ]); + expect([ + ...queries.posts.recent({ limit: 5, excludeSlug: "a" }).queryKey, + ]).toEqual(["posts", "recent", "recent", { limit: 5, excludeSlug: "a" }]); + }); +}); diff --git a/packages/stack/src/__tests__/resource-factory.test.tsx b/packages/stack/src/__tests__/resource-factory.test.tsx new file mode 100644 index 000000000..6fd985218 --- /dev/null +++ b/packages/stack/src/__tests__/resource-factory.test.tsx @@ -0,0 +1,461 @@ +// @vitest-environment jsdom +import { act, Suspense } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { StackProvider } from "../context"; +import { + createResourceQueryKeys, + runResourceMutation, + type ResourcesDeclaration, + type StackError, +} from "../plugins/client"; +import { createResource } from "../plugins/client/hooks"; + +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +interface Item { + id: string; + name: string; +} + +interface ListParams { + q?: string; + limit?: number; +} + +const resources = { + items: { + queries: { + list: { + path: "/items", + query: (params?: ListParams) => ({ + q: params?.q, + limit: params?.limit ?? 10, + }), + key: (params?: ListParams) => [ + { q: params?.q, limit: params?.limit ?? 10 }, + ], + select: (data: any): Item[] => data?.items ?? [], + infinite: true, + pageSize: (params?: ListParams) => params?.limit ?? 10, + }, + detail: { + path: "/items", + query: (id: string) => ({ id, limit: 1 }), + key: (id: string) => [id], + select: (data: any): Item | null => data?.items?.[0] ?? null, + skip: (id: string) => !id, + }, + all: { + path: "/all-items", + key: () => ["all"], + select: (data: any): Item[] => data ?? [], + }, + }, + mutations: { + create: { + path: "@post/items", + method: "POST" as const, + input: (vars: { name: string }) => ({ body: vars }), + select: (data: any) => data as Item | null, + invalidates: ["items.list"], + setData: { + args: (result: Item | null) => (result?.id ? [result.id] : null), + }, + }, + remove: { + path: "@delete/items/:id", + method: "DELETE" as const, + input: (vars: { id: string }) => ({ params: { id: vars.id } }), + select: (data: any) => data as { success: boolean }, + invalidates: ["items"], + }, + }, + }, +} satisfies ResourcesDeclaration; + +describe("createResourceQueryKeys", () => { + const client = vi.fn(); + + beforeEach(() => { + client.mockReset(); + }); + + it("produces query-key-factory compatible shapes", () => { + const keys = createResourceQueryKeys(client, resources); + + expect(keys.items._def).toEqual(["items"]); + expect(keys.items.list._def).toEqual(["items", "list"]); + expect(keys.items.detail._def).toEqual(["items", "detail"]); + + expect(keys.items.list({ q: "x" }).queryKey).toEqual([ + "items", + "list", + { q: "x", limit: 10 }, + ]); + expect(keys.items.detail("42").queryKey).toEqual(["items", "detail", "42"]); + expect(keys.items.all().queryKey).toEqual(["items", "all", "all"]); + }); + + it("defaults key cells to the args when no key fn is declared", () => { + const keys = createResourceQueryKeys(client, { + things: { + queries: { byId: { path: "/things", query: (id: string) => ({ id }) } }, + }, + }); + expect(keys.things.byId("7").queryKey).toEqual(["things", "byId", "7"]); + }); + + it("fetches, unwraps and selects data", async () => { + client.mockResolvedValue({ + data: { items: [{ id: "1", name: "one" }] }, + }); + const keys = createResourceQueryKeys(client, resources, { + "x-test": "yes", + }); + + const result = await keys.items.detail("1").queryFn(); + + expect(result).toEqual({ id: "1", name: "one" }); + expect(client).toHaveBeenCalledWith("/items", { + method: "GET", + query: { id: "1", limit: 1 }, + headers: { "x-test": "yes" }, + }); + }); + + it("injects the page offset for infinite queries", async () => { + client.mockResolvedValue({ data: { items: [] } }); + const keys = createResourceQueryKeys(client, resources); + + await keys.items.list({ q: "a", limit: 5 }).queryFn({ pageParam: 15 }); + + expect(client).toHaveBeenCalledWith("/items", { + method: "GET", + query: { q: "a", limit: 5, offset: 15 }, + }); + + // Defaults to offset 0 when no pageParam is provided + await keys.items.list({ limit: 5 }).queryFn(); + expect(client).toHaveBeenLastCalledWith("/items", { + method: "GET", + query: { q: undefined, limit: 5, offset: 0 }, + }); + }); + + it("throws a normalized StackError on error responses", async () => { + client.mockResolvedValue({ + error: { message: "denied", status: 403 }, + }); + const keys = createResourceQueryKeys(client, resources); + + await expect(keys.items.detail("1").queryFn()).rejects.toMatchObject({ + message: "denied", + statusCode: 403, + }); + }); + + it("skips fetching and resolves null when skip matches", async () => { + const keys = createResourceQueryKeys(client, resources); + + await expect(keys.items.detail("").queryFn()).resolves.toBeNull(); + expect(client).not.toHaveBeenCalled(); + }); +}); + +describe("runResourceMutation", () => { + it("maps vars through input and unwraps the result", async () => { + const client = vi.fn().mockResolvedValue({ data: { success: true } }); + + const result = await runResourceMutation( + client, + resources.items.mutations.remove, + { id: "9" }, + ); + + expect(result).toEqual({ success: true }); + expect(client).toHaveBeenCalledWith("@delete/items/:id", { + method: "DELETE", + params: { id: "9" }, + }); + }); + + it("defaults to sending vars as the body", async () => { + const client = vi.fn().mockResolvedValue({ data: { id: "1" } }); + + await runResourceMutation( + client, + { path: "@post/items", method: "POST" }, + { name: "x" }, + ); + + expect(client).toHaveBeenCalledWith("@post/items", { + method: "POST", + body: { name: "x" }, + }); + }); + + it("throws a normalized StackError with field errors", async () => { + const client = vi.fn().mockResolvedValue({ + error: { + message: "[body.name] Required", + status: 400, + issues: [{ path: ["name"], message: "Required" }], + }, + }); + + try { + await runResourceMutation(client, resources.items.mutations.create, { + name: "", + }); + expect.unreachable("mutation should reject"); + } catch (error) { + const stackError = error as StackError; + expect(stackError.statusCode).toBe(400); + expect(stackError.errors).toEqual({ name: "Required" }); + } + }); +}); + +describe("createResource hooks", () => { + const items = createResource({ plugin: "test-plugin", resources }); + + let container: HTMLDivElement; + let root: Root; + let queryClient: QueryClient; + let refresh: ReturnType; + let fetchMock: ReturnType; + + beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + root = createRoot(container); + queryClient = new QueryClient(); + refresh = vi.fn(); + fetchMock = vi.spyOn(globalThis, "fetch" as any); + }); + + afterEach(async () => { + await act(async () => { + root.unmount(); + }); + container.remove(); + vi.restoreAllMocks(); + }); + + function jsonResponse(body: unknown, status = 200) { + return new Response(JSON.stringify(body), { + status, + headers: { "content-type": "application/json" }, + }); + } + + async function render(ui: React.ReactElement) { + await act(async () => { + root.render( + + {ui} + , + ); + }); + } + + async function waitFor(check: () => boolean, timeout = 3000) { + const start = Date.now(); + while (!check()) { + if (Date.now() - start > timeout) { + throw new Error("waitFor timed out"); + } + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 10)); + }); + } + } + + it("use() fetches and selects data", async () => { + fetchMock.mockResolvedValue( + jsonResponse({ items: [{ id: "1", name: "one" }] }), + ); + + let captured: any; + function Probe() { + captured = items.items.detail.use(["1"]); + return null; + } + await render(); + await waitFor(() => captured.isSuccess); + + expect(captured.data).toEqual({ id: "1", name: "one" }); + const url = String(fetchMock.mock.calls[0]?.[0]); + expect(url).toContain("http://test.local/api/data/items"); + expect(url).toContain("id=1"); + }); + + it("use() respects the enabled option", async () => { + let captured: any; + function Probe() { + captured = items.items.detail.use(["1"], { enabled: false }); + return null; + } + await render(); + + expect(captured.fetchStatus).toBe("idle"); + expect(fetchMock).not.toHaveBeenCalled(); + }); + + it("useSuspense() suspends and resolves data", async () => { + fetchMock.mockResolvedValue( + jsonResponse({ items: [{ id: "1", name: "one" }] }), + ); + + let captured: any; + function Probe() { + captured = items.items.detail.useSuspense(["1"]); + return null; + } + await render( + + + , + ); + await waitFor(() => !!captured?.data); + + expect(captured.data).toEqual({ id: "1", name: "one" }); + }); + + it("useInfinite() pages by offset and derives hasNextPage from pageSize", async () => { + const firstPage = Array.from({ length: 2 }, (_, i) => ({ + id: `a${i}`, + name: `a${i}`, + })); + const secondPage = [{ id: "b0", name: "b0" }]; + fetchMock.mockImplementation(async (input: any) => { + const url = String(input); + return url.includes("offset=2") + ? jsonResponse({ items: secondPage }) + : jsonResponse({ items: firstPage }); + }); + + let captured: any; + function Probe() { + captured = items.items.list.useInfinite([{ limit: 2 }]); + return null; + } + await render(); + await waitFor(() => captured.isSuccess); + + expect(captured.data.pages).toEqual([firstPage]); + expect(captured.hasNextPage).toBe(true); + + await act(async () => { + await captured.fetchNextPage(); + }); + await waitFor(() => captured.data.pages.length === 2); + + expect(captured.data.pages[1]).toEqual(secondPage); + // Second page is short (1 < 2) — no further pages + expect(captured.hasNextPage).toBe(false); + expect(String(fetchMock.mock.calls[1]?.[0])).toContain("offset=2"); + }); + + it("mutations invalidate declared targets, seed detail data and refresh", async () => { + const created: Item = { id: "42", name: "created" }; + fetchMock.mockResolvedValue(jsonResponse(created)); + + // Seed a list entry so we can observe invalidation + const listKey = ["items", "list", { q: undefined, limit: 10 }]; + queryClient.setQueryData(listKey, { pages: [[]], pageParams: [0] }); + + let captured: any; + function Probe() { + captured = items.items.create.use(); + return null; + } + await render(); + + await act(async () => { + await captured.mutateAsync({ name: "created" }); + }); + + // POST issued to the right endpoint + const [url, init] = fetchMock.mock.calls[0] as [unknown, RequestInit]; + expect(String(url)).toContain("/api/data/items"); + expect(init.method).toBe("POST"); + + // Detail cache seeded from the result + expect(queryClient.getQueryData(["items", "detail", "42"])).toEqual( + created, + ); + // List invalidated + expect(queryClient.getQueryState(listKey)?.isInvalidated).toBe(true); + // refresh called after invalidation + expect(refresh).toHaveBeenCalledTimes(1); + }); + + it("resource-level invalidation targets every query of the resource", async () => { + fetchMock.mockResolvedValue(jsonResponse({ success: true })); + + const listKey = ["items", "list", { q: undefined, limit: 10 }]; + const detailKey = ["items", "detail", "9"]; + queryClient.setQueryData(listKey, { pages: [[]], pageParams: [0] }); + queryClient.setQueryData(detailKey, { id: "9", name: "nine" }); + + let captured: any; + function Probe() { + captured = items.items.remove.use(); + return null; + } + await render(); + + await act(async () => { + await captured.mutateAsync({ id: "9" }); + }); + + expect(queryClient.getQueryState(listKey)?.isInvalidated).toBe(true); + expect(queryClient.getQueryState(detailKey)?.isInvalidated).toBe(true); + }); + + it("mutations reject with a normalized StackError", async () => { + fetchMock.mockResolvedValue( + jsonResponse( + { + message: "[body.name] Required", + code: "VALIDATION_ERROR", + issues: [{ path: ["name"], message: "Required" }], + }, + 400, + ), + ); + + let captured: any; + function Probe() { + captured = items.items.create.use(); + return null; + } + await render(); + + let thrown: StackError | undefined; + await act(async () => { + try { + await captured.mutateAsync({ name: "" }); + } catch (error) { + thrown = error as StackError; + } + }); + + expect(thrown).toBeDefined(); + expect(thrown?.errors).toEqual({ name: "Required" }); + // No invalidation or refresh on failure + expect(refresh).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/stack/src/__tests__/resource-use-form.test.tsx b/packages/stack/src/__tests__/resource-use-form.test.tsx new file mode 100644 index 000000000..fd7d77e42 --- /dev/null +++ b/packages/stack/src/__tests__/resource-use-form.test.tsx @@ -0,0 +1,352 @@ +// @vitest-environment jsdom +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { StackProvider } from "../context"; +import type { ResourcesDeclaration } from "../plugins/client"; +import { createResource } from "../plugins/client/hooks"; +import type { ResourceFormResult } from "../plugins/client/hooks"; + +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +interface Item { + id: string; + slug: string; + name: string; +} + +interface FormValues { + name: string; +} + +const resources = { + items: { + queries: { + detail: { + path: "/items", + query: (slug: string) => ({ slug, limit: 1 }), + key: (slug: string) => [slug], + select: (data: any): Item | null => data?.items?.[0] ?? null, + skip: (slug: string) => !slug, + }, + }, + mutations: { + create: { + path: "@post/items", + method: "POST" as const, + input: (vars: { name: string }) => ({ body: vars }), + select: (data: any) => data as Item | null, + invalidates: ["items"], + }, + update: { + path: "@put/items/:id", + method: "PUT" as const, + input: (vars: { id: string; data: { name: string } }) => ({ + params: { id: vars.id }, + body: vars.data, + }), + select: (data: any) => data as Item | null, + invalidates: ["items"], + }, + }, + }, +} satisfies ResourcesDeclaration; + +const items = createResource({ plugin: "test-plugin", resources }); + +type FormHookConfig = Parameters>[0]; + +describe("resource useForm", () => { + let container: HTMLDivElement; + let root: Root; + let queryClient: QueryClient; + let navigate: ReturnType; + let refresh: ReturnType; + let notifySuccess: ReturnType; + let notifyError: ReturnType; + let fetchMock: ReturnType; + + beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + root = createRoot(container); + queryClient = new QueryClient(); + navigate = vi.fn(); + refresh = vi.fn(); + notifySuccess = vi.fn(); + notifyError = vi.fn(); + fetchMock = vi.spyOn(globalThis, "fetch" as any); + }); + + afterEach(async () => { + await act(async () => { + root.unmount(); + }); + container.remove(); + vi.restoreAllMocks(); + }); + + function jsonResponse(body: unknown, status = 200) { + return new Response(JSON.stringify(body), { + status, + headers: { "content-type": "application/json" }, + }); + } + + async function renderForm( + config: FormHookConfig, + onCapture: (form: ResourceFormResult) => void, + ) { + function Probe() { + const form = items.items.useForm(config); + onCapture(form as ResourceFormResult); + return null; + } + await act(async () => { + root.render( + + + + + , + ); + }); + } + + async function waitFor(check: () => boolean, timeout = 3000) { + const start = Date.now(); + while (!check()) { + if (Date.now() - start > timeout) { + throw new Error("waitFor timed out"); + } + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 10)); + }); + } + } + + it("create: submits the create mutation, notifies and redirects", async () => { + const created: Item = { id: "1", slug: "one", name: "One" }; + fetchMock.mockResolvedValue(jsonResponse(created)); + + let form!: ResourceFormResult; + await renderForm( + { + action: "create", + successMessage: "Item saved", + redirect: (result) => `/items/${(result as Item).slug}`, + }, + (value) => { + form = value; + }, + ); + + expect(form.record).toBeNull(); + expect(form.isLoadingRecord).toBe(false); + + let result: unknown; + await act(async () => { + result = await form.submit({ name: "One" }); + }); + + expect(result).toEqual(created); + const [url, init] = fetchMock.mock.calls[0] as [unknown, RequestInit]; + expect(String(url)).toContain("/api/data/items"); + expect(init.method).toBe("POST"); + expect(notifySuccess).toHaveBeenCalledWith("Item saved"); + expect(navigate).toHaveBeenCalledWith("/items/one"); + expect(refresh).toHaveBeenCalledTimes(1); + expect(form.error).toBeNull(); + expect(form.fieldErrors).toEqual({}); + }); + + it("edit: fetches the record, derives defaults and submits the update mutation", async () => { + const existing: Item = { id: "9", slug: "nine", name: "Nine" }; + fetchMock.mockImplementation(async (input: any, init?: any) => { + void input; + const method = (init as RequestInit | undefined)?.method; + if (!method || method === "GET") { + return jsonResponse({ items: [existing] }); + } + return jsonResponse({ ...existing, name: "Nine v2" }); + }); + + let form!: ResourceFormResult; + await renderForm( + { + action: "edit", + id: "nine", + defaults: (record) => ({ name: (record as Item | null)?.name ?? "" }), + toUpdateVars: (values, record) => ({ + id: (record as Item).id, + data: values, + }), + successMessage: "Item updated", + }, + (value) => { + form = value; + }, + ); + + await waitFor(() => form.record !== null); + expect(form.record).toEqual(existing); + expect(form.defaultValues).toEqual({ name: "Nine" }); + + await act(async () => { + await form.submit({ name: "Nine v2" }); + }); + + const putCall = fetchMock.mock.calls.find( + (call) => (call[1] as RequestInit | undefined)?.method === "PUT", + ); + expect(putCall).toBeDefined(); + expect(String(putCall?.[0])).toContain("/api/data/items/9"); + expect(notifySuccess).toHaveBeenCalledWith("Item updated"); + }); + + it("edit: uses an externally supplied record without fetching", async () => { + const external: Item = { id: "5", slug: "five", name: "Five" }; + + let form!: ResourceFormResult; + await renderForm( + { + action: "edit", + record: external, + defaults: (record) => ({ name: (record as Item | null)?.name ?? "" }), + }, + (value) => { + form = value; + }, + ); + + expect(form.record).toEqual(external); + expect(form.defaultValues).toEqual({ name: "Five" }); + expect(fetchMock).not.toHaveBeenCalled(); + }); + + it("maps server validation issues onto fieldErrors without a toast", async () => { + fetchMock.mockResolvedValue( + jsonResponse( + { + message: "[body.name] Name is required", + code: "VALIDATION_ERROR", + issues: [{ path: ["name"], message: "Name is required" }], + }, + 400, + ), + ); + + let form!: ResourceFormResult; + await renderForm( + { + action: "create", + successMessage: "saved", + errorMessage: "Failed to save", + }, + (value) => { + form = value; + }, + ); + + let result: unknown = "sentinel"; + await act(async () => { + result = await form.submit({ name: "" }); + }); + + expect(result).toBeUndefined(); + expect(form.fieldErrors).toEqual({ name: "Name is required" }); + expect(form.error?.statusCode).toBe(400); + // Field-level failures do not produce a generic toast + expect(notifyError).not.toHaveBeenCalled(); + expect(notifySuccess).not.toHaveBeenCalled(); + expect(navigate).not.toHaveBeenCalled(); + }); + + it("notifies errorMessage for non-field errors", async () => { + fetchMock.mockResolvedValue( + jsonResponse({ message: "Internal error" }, 500), + ); + + let form!: ResourceFormResult; + await renderForm( + { + action: "create", + errorMessage: (error) => `Save failed: ${error.message}`, + }, + (value) => { + form = value; + }, + ); + + await act(async () => { + await form.submit({ name: "x" }); + }); + + expect(form.error?.message).toBe("Internal error"); + expect(form.fieldErrors).toEqual({}); + expect(notifyError).toHaveBeenCalledWith("Save failed: Internal error"); + }); + + it("clearErrors resets the error state", async () => { + fetchMock.mockResolvedValue( + jsonResponse( + { + message: "invalid", + issues: [{ path: ["name"], message: "Bad" }], + }, + 400, + ), + ); + + let form!: ResourceFormResult; + await renderForm({ action: "create" }, (value) => { + form = value; + }); + + await act(async () => { + await form.submit({ name: "" }); + }); + expect(form.fieldErrors).toEqual({ name: "Bad" }); + + await act(async () => { + form.clearErrors(); + }); + expect(form.error).toBeNull(); + expect(form.fieldErrors).toEqual({}); + }); + + it("skips navigation when redirect resolves falsy", async () => { + fetchMock.mockResolvedValue( + jsonResponse({ id: "1", slug: "one", name: "One" }), + ); + + let form!: ResourceFormResult; + await renderForm( + { + action: "create", + redirect: () => false, + }, + (value) => { + form = value; + }, + ); + + await act(async () => { + await form.submit({ name: "One" }); + }); + + expect(navigate).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/stack/src/__tests__/resource-use-select.test.tsx b/packages/stack/src/__tests__/resource-use-select.test.tsx new file mode 100644 index 000000000..ec60f21d5 --- /dev/null +++ b/packages/stack/src/__tests__/resource-use-select.test.tsx @@ -0,0 +1,248 @@ +// @vitest-environment jsdom +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { StackProvider } from "../context"; +import type { ResourcesDeclaration } from "../plugins/client"; +import { createResource } from "../plugins/client/hooks"; +import type { ResourceSelectResult } from "../plugins/client/hooks"; + +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +interface Tag { + id: string; + name: string; +} + +const resources = { + tags: { + queries: { + list: { + path: "/tags", + query: (search?: string) => ({ q: search || undefined }), + key: (search?: string) => [{ q: search || undefined }], + select: (data: any): Tag[] => data ?? [], + }, + detail: { + path: "/tags/one", + query: (id: string) => ({ id }), + key: (id: string) => [id], + select: (data: any): Tag | null => data ?? null, + }, + }, + }, +} satisfies ResourcesDeclaration; + +const tags = createResource({ plugin: "test-plugin", resources }); + +const ALL_TAGS: Tag[] = [ + { id: "1", name: "alpha" }, + { id: "2", name: "beta" }, + { id: "3", name: "kanban" }, +]; + +describe("resource useSelect", () => { + let container: HTMLDivElement; + let root: Root; + let queryClient: QueryClient; + let fetchMock: ReturnType; + + beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + root = createRoot(container); + queryClient = new QueryClient(); + fetchMock = vi.spyOn(globalThis, "fetch" as any); + fetchMock.mockImplementation(async (input: any) => { + const url = new URL(String(input)); + if (url.pathname.endsWith("/tags/one")) { + const id = url.searchParams.get("id"); + const tag = id === "42" ? { id: "42", name: "preloaded" } : null; + return jsonResponse(tag); + } + const q = url.searchParams.get("q"); + const filtered = q + ? ALL_TAGS.filter((tag) => tag.name.includes(q)) + : ALL_TAGS; + return jsonResponse(filtered); + }); + }); + + afterEach(async () => { + await act(async () => { + root.unmount(); + }); + container.remove(); + vi.restoreAllMocks(); + }); + + function jsonResponse(body: unknown, status = 200) { + return new Response(JSON.stringify(body), { + status, + headers: { "content-type": "application/json" }, + }); + } + + type SelectConfig = Parameters>[0]; + + async function renderSelect( + config: SelectConfig, + onCapture: (result: ResourceSelectResult) => void, + ) { + function Probe() { + const result = tags.tags.useSelect(config); + onCapture(result); + return null; + } + await act(async () => { + root.render( + + + + + , + ); + }); + } + + async function waitFor(check: () => boolean, timeout = 3000) { + const start = Date.now(); + while (!check()) { + if (Date.now() - start > timeout) { + throw new Error("waitFor timed out"); + } + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 10)); + }); + } + } + + const baseConfig: SelectConfig = { + searchArgs: (search) => [search], + getOptionValue: (tag) => tag.id, + getOptionLabel: (tag) => tag.name, + debounceMs: 50, + }; + + it("loads initial options for the empty search", async () => { + let select!: ResourceSelectResult; + await renderSelect(baseConfig, (value) => { + select = value; + }); + await waitFor(() => !select.isLoading); + + expect(select.options).toEqual([ + { value: "1", label: "alpha", item: ALL_TAGS[0] }, + { value: "2", label: "beta", item: ALL_TAGS[1] }, + { value: "3", label: "kanban", item: ALL_TAGS[2] }, + ]); + expect(select.isSearching).toBe(false); + }); + + it("debounces the search text and skips intermediate values", async () => { + let select!: ResourceSelectResult; + await renderSelect(baseConfig, (value) => { + select = value; + }); + await waitFor(() => !select.isLoading); + + await act(async () => { + select.setSearch("k"); + }); + await act(async () => { + select.setSearch("ka"); + }); + + // Debounce pending: still searching, no new fetch yet + expect(select.isSearching).toBe(true); + const callsBefore = fetchMock.mock.calls.length; + + await waitFor( + () => !select.isSearching && select.options.length === 1, + 2000, + ); + + expect(select.options[0]?.label).toBe("kanban"); + + // Only the debounced value was fetched — never the intermediate "k" + const searchedTerms = fetchMock.mock.calls + .slice(callsBefore) + .map((call) => new URL(String(call[0])).searchParams.get("q")); + expect(searchedTerms).toEqual(["ka"]); + }); + + it("preloads selected values missing from the options", async () => { + let select!: ResourceSelectResult; + await renderSelect( + { + ...baseConfig, + value: "42", + preload: { args: (value) => [value] }, + }, + (value) => { + select = value; + }, + ); + await waitFor( + () => select.options.some((option) => option.value === "42"), + 2000, + ); + + expect(select.options).toContainEqual({ + value: "42", + label: "preloaded", + item: { id: "42", name: "preloaded" }, + }); + expect(select.selectedOptions).toEqual([ + { + value: "42", + label: "preloaded", + item: { id: "42", name: "preloaded" }, + }, + ]); + }); + + it("does not preload values already present in the options", async () => { + let select!: ResourceSelectResult; + await renderSelect( + { + ...baseConfig, + value: "2", + preload: { args: (value) => [value] }, + }, + (value) => { + select = value; + }, + ); + await waitFor(() => !select.isLoading); + + expect(select.selectedOptions).toEqual([ + { value: "2", label: "beta", item: ALL_TAGS[1] }, + ]); + const detailCalls = fetchMock.mock.calls.filter((call) => + String(call[0]).includes("/tags/one"), + ); + expect(detailCalls).toHaveLength(0); + }); + + it("falls back to the raw value for unresolvable selections", async () => { + let select!: ResourceSelectResult; + await renderSelect({ ...baseConfig, value: "missing" }, (value) => { + select = value; + }); + await waitFor(() => !select.isLoading); + + expect(select.selectedOptions).toEqual([ + { value: "missing", label: "missing" }, + ]); + }); +}); diff --git a/packages/stack/src/__tests__/stack-error.test.ts b/packages/stack/src/__tests__/stack-error.test.ts new file mode 100644 index 000000000..9c2318d41 --- /dev/null +++ b/packages/stack/src/__tests__/stack-error.test.ts @@ -0,0 +1,174 @@ +import { describe, expect, it } from "vitest"; +import { z } from "zod"; +import { createRouter } from "better-call"; +import { createEndpoint } from "../plugins/api"; +import { isErrorResponse, toError, type StackError } from "../plugins/client"; + +describe("isErrorResponse", () => { + it("detects better-call error responses", () => { + expect(isErrorResponse({ error: { message: "boom" } })).toBe(true); + expect(isErrorResponse({ error: null, data: [] })).toBe(false); + expect(isErrorResponse({ data: [] })).toBe(false); + expect(isErrorResponse(null)).toBe(false); + expect(isErrorResponse("nope")).toBe(false); + }); +}); + +describe("toError", () => { + it("passes through Error instances", () => { + const original = new Error("original"); + expect(toError(original)).toBe(original); + }); + + it("extracts message from object errors and preserves properties", () => { + const error = toError({ message: "boom", code: "SOME_CODE" }); + expect(error).toBeInstanceOf(Error); + expect(error.message).toBe("boom"); + expect((error as StackError & { code?: string }).code).toBe("SOME_CODE"); + }); + + it("falls back to the error string property, then JSON", () => { + expect(toError({ error: "denied" }).message).toBe("denied"); + expect(toError({ foo: 1 }).message).toBe('{"foo":1}'); + }); + + it("wraps primitive values", () => { + expect(toError("nope").message).toBe("nope"); + expect(toError(42).message).toBe("42"); + }); + + it("normalizes statusCode from status or statusCode", () => { + expect(toError({ message: "x", status: 404 }).statusCode).toBe(404); + expect(toError({ message: "x", statusCode: 403 }).statusCode).toBe(403); + expect(toError({ message: "x" }).statusCode).toBeUndefined(); + }); + + it("maps validation issues to field errors", () => { + const error = toError({ + message: "[body.title] Required; [body.tags.0.name] Too short", + code: "VALIDATION_ERROR", + issues: [ + { path: ["title"], message: "Required" }, + { path: ["tags", 0, "name"], message: "Too short" }, + ], + }); + expect(error.errors).toEqual({ + title: "Required", + "tags.0.name": "Too short", + }); + }); + + it("collects multiple issues on the same field into an array", () => { + const error = toError({ + message: "invalid", + issues: [ + { path: ["slug"], message: "Too short" }, + { path: ["slug"], message: "Invalid characters" }, + ], + }); + expect(error.errors).toEqual({ + slug: ["Too short", "Invalid characters"], + }); + }); + + it("skips issues without a path", () => { + const error = toError({ + message: "invalid", + issues: [{ path: [], message: "Something is wrong" }], + }); + expect(error.errors).toBeUndefined(); + }); + + it("keeps a pre-shaped errors record", () => { + const error = toError({ + message: "invalid", + errors: { title: "Required", tags: ["a", "b"] }, + }); + expect(error.errors).toEqual({ title: "Required", tags: ["a", "b"] }); + }); + + it("drops a non-conforming errors property", () => { + const error = toError({ message: "invalid", errors: 42 }); + expect(error.errors).toBeUndefined(); + }); +}); + +describe("createEndpoint validation issue preservation", () => { + const createItem = createEndpoint( + "/items", + { + method: "POST", + body: z.object({ + title: z.string().min(1, "Title is required"), + count: z.number(), + }), + }, + async (ctx) => ctx.body, + ); + + it("includes serialized Zod issues in the 400 response body", async () => { + const router = createRouter({ createItem }); + const response = await router.handler( + new Request("http://localhost/items", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ title: "", count: "not-a-number" }), + }), + ); + + expect(response.status).toBe(400); + const body = (await response.json()) as Record; + expect(body.code).toBe("VALIDATION_ERROR"); + expect(Array.isArray(body.issues)).toBe(true); + + // Round-trip: the response body maps onto StackError field errors + const error = toError({ ...body, status: response.status }); + expect(error.statusCode).toBe(400); + expect(error.errors).toBeDefined(); + expect(error.errors?.title).toBe("Title is required"); + expect(error.errors?.count).toBeDefined(); + }); + + it("keeps valid requests working", async () => { + const router = createRouter({ createItem }); + const response = await router.handler( + new Request("http://localhost/items", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ title: "hello", count: 2 }), + }), + ); + + expect(response.status).toBe(200); + expect(await response.json()).toEqual({ title: "hello", count: 2 }); + }); + + it("respects a user-provided onValidationError", async () => { + let observed: unknown; + const custom = createEndpoint( + "/custom", + { + method: "POST", + body: z.object({ name: z.string() }), + onValidationError: (error: unknown) => { + observed = error; + }, + }, + async (ctx) => ctx.body, + ); + const router = createRouter({ custom }); + const response = await router.handler( + new Request("http://localhost/custom", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({}), + }), + ); + + // Default better-call behavior: 400 without issues in the body + expect(response.status).toBe(400); + const body = (await response.json()) as Record; + expect(body.issues).toBeUndefined(); + expect(observed).toBeDefined(); + }); +}); diff --git a/packages/stack/src/plugins/api/create-endpoint.ts b/packages/stack/src/plugins/api/create-endpoint.ts new file mode 100644 index 000000000..389e303b2 --- /dev/null +++ b/packages/stack/src/plugins/api/create-endpoint.ts @@ -0,0 +1,82 @@ +import { APIError, createEndpoint as baseCreateEndpoint } from "better-call"; + +/** + * Validation issue segment shapes produced by standard-schema validators + * (better-call runs Zod through the standard-schema interface). + */ +type IssuePathSegment = PropertyKey | { key: PropertyKey }; + +interface StandardIssue { + message: string; + path?: ReadonlyArray; +} + +/** JSON-safe issue shape included in 400 validation error response bodies. */ +export interface SerializedValidationIssue { + message: string; + path: Array; +} + +function serializeIssues(issues: unknown): SerializedValidationIssue[] { + if (!Array.isArray(issues)) return []; + return (issues as StandardIssue[]) + .filter((issue) => typeof issue?.message === "string") + .map((issue) => ({ + message: issue.message, + path: (issue.path ?? []).map((segment) => { + const key = + typeof segment === "object" && segment !== null + ? segment.key + : segment; + return typeof key === "number" ? key : String(key); + }), + })); +} + +/** + * Drop-in replacement for better-call's `createEndpoint` that preserves + * Zod field-level validation issues in error responses. + * + * better-call discards `ValidationError.issues` when building the default + * 400 response (only the flattened message survives). Its `onValidationError` + * callback runs before that default and may throw a replacement error, so we + * inject one that re-throws the same 400 `APIError` with a JSON-safe `issues` + * array added to the body. Clients can then map issues onto form fields (see + * `toError` in `@btst/stack/plugins/client`). + * + * Endpoints that define their own `onValidationError` are left untouched. + */ +export const createEndpoint = (( + pathOrOptions: any, + handlerOrOptions: any, + handlerOrNever?: any, +) => { + const isPathForm = typeof pathOrOptions === "string"; + const options = isPathForm ? handlerOrOptions : pathOrOptions; + + const wrappedOptions = + options && typeof options === "object" && !options.onValidationError + ? { + ...options, + onValidationError: ({ + message, + issues, + }: { + message: string; + issues: unknown; + }) => { + throw new APIError(400, { + message, + code: "VALIDATION_ERROR", + issues: serializeIssues(issues), + }); + }, + } + : options; + + return isPathForm + ? baseCreateEndpoint(pathOrOptions, wrappedOptions, handlerOrNever) + : baseCreateEndpoint(wrappedOptions, handlerOrOptions); +}) as typeof baseCreateEndpoint; + +createEndpoint.create = baseCreateEndpoint.create; diff --git a/packages/stack/src/plugins/api/index.ts b/packages/stack/src/plugins/api/index.ts index 0ef81180a..e907cb64c 100644 --- a/packages/stack/src/plugins/api/index.ts +++ b/packages/stack/src/plugins/api/index.ts @@ -26,7 +26,10 @@ export type { // Re-export Better Call functions needed for plugins export type { Endpoint, Router } from "better-call"; -export { createEndpoint, createRouter } from "better-call"; +export { createRouter } from "better-call"; +// Wrapped createEndpoint that preserves Zod validation issues in 400 responses +export { createEndpoint } from "./create-endpoint"; +export type { SerializedValidationIssue } from "./create-endpoint"; export { createDbPlugin } from "@btst/db"; /** diff --git a/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx b/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx index a98ad202c..1f25a55a6 100644 --- a/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx +++ b/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx @@ -19,13 +19,10 @@ import { Input } from "@workspace/ui/components/input"; import { Switch } from "@workspace/ui/components/switch"; import { Textarea } from "@workspace/ui/components/textarea"; -import { - useCreatePost, - useSuspensePost, - useUpdatePost, - useDeletePost, -} from "../../hooks/blog-hooks"; +import { useSuspensePost, useDeletePost } from "../../hooks/blog-hooks"; +import { blog } from "../../hooks/blog-resource"; import { slugify } from "../../../utils"; +import type { SerializedPost } from "../../../types"; import { AlertDialog, AlertDialogAction, @@ -40,14 +37,14 @@ import { import { zodResolver } from "@hookform/resolvers/zod"; import { Loader2 } from "lucide-react"; -import { lazy, memo, Suspense, useEffect, useMemo, useState } from "react"; +import { lazy, memo, Suspense, useEffect, useState } from "react"; import { type FieldPath, + type FieldValues, type SubmitHandler, type UseFormReturn, useForm, } from "react-hook-form"; -import { toast } from "sonner"; import { z } from "zod"; import { FeaturedImageField } from "./image-field"; @@ -57,11 +54,29 @@ const MarkdownEditor = lazy(() => })), ); import { BLOG_LOCALIZATION } from "../../localization"; -import { usePluginOverrides } from "@btst/stack/context"; +import { useNotify, usePluginOverrides } from "@btst/stack/context"; import type { BlogPluginOverrides } from "../../overrides"; import { EmptyList } from "../shared/empty-list"; import { TagsMultiSelect } from "./tags-multiselect"; +/** + * Applies server-side field validation errors (from `StackError.errors`) + * onto react-hook-form field state. + */ +function useServerFieldErrors( + form: UseFormReturn, + fieldErrors: Record, +) { + useEffect(() => { + for (const [field, message] of Object.entries(fieldErrors)) { + form.setError(field as FieldPath, { + type: "server", + message: Array.isArray(message) ? message.join(", ") : message, + }); + } + }, [fieldErrors, form]); +} + type CommonPostFormValues = { title: string; content: string; @@ -358,33 +373,33 @@ const AddPostFormComponent = ({ const schema = CustomPostCreateSchema; - const { - mutateAsync: createPost, - isPending: isCreatingPost, - error: createPostError, - } = useCreatePost(); - type AddPostFormValues = z.input; - const onSubmit = async (data: AddPostFormValues) => { - // Auto-generate slug from title if not provided - const slug = data.slug || slugify(data.title); - // Wait for mutation to complete, including refresh - const createdPost = await createPost({ + const resourceForm = blog.posts.useForm< + AddPostFormValues, + SerializedPost | null + >({ + action: "create", + successMessage: localization.BLOG_FORMS_TOAST_CREATE_SUCCESS, + toCreateVars: (data) => ({ title: data.title, content: data.content, excerpt: data.excerpt ?? "", - slug, + // Auto-generate slug from title if not provided + slug: data.slug || slugify(data.title), published: data.published ?? false, publishedAt: data.published ? new Date() : undefined, image: data.image, tags: data.tags || [], - }); - - toast.success(localization.BLOG_FORMS_TOAST_CREATE_SUCCESS); + }), + onSuccess: (createdPost) => { + // Navigate only after mutation (including invalidation) completes + onSuccess({ published: createdPost?.published ?? false }); + }, + }); - // Navigate only after mutation completes - onSuccess({ published: createdPost?.published ?? false }); + const onSubmit = async (data: AddPostFormValues) => { + await resourceForm.submit(data); }; // For compatibility with resolver types that require certain required fields, @@ -402,6 +417,10 @@ const AddPostFormComponent = ({ }, }); + // Server-side Zod validation failures land on the matching form fields + useServerFieldErrors(form, resourceForm.fieldErrors); + const hasFieldErrors = Object.keys(resourceForm.fieldErrors).length > 0; + // Expose form instance to parent for AI context integration useEffect(() => { onFormReady?.(form); @@ -413,13 +432,13 @@ const AddPostFormComponent = ({ form={form} onSubmit={onSubmit} submitLabel={ - isCreatingPost + resourceForm.isSubmitting ? localization.BLOG_FORMS_SUBMIT_CREATE_PENDING : localization.BLOG_FORMS_SUBMIT_CREATE_IDLE } onCancel={onClose} - disabled={isCreatingPost || featuredImageUploading} - errorMessage={createPostError?.message} + disabled={resourceForm.isSubmitting || featuredImageUploading} + errorMessage={hasFieldErrors ? undefined : resourceForm.error?.message} setFeaturedImageUploading={setFeaturedImageUploading} /> ); @@ -468,72 +487,85 @@ const EditPostFormComponent = ({ // const { uploadImage } = useBlogContext() const { post } = useSuspensePost(postSlug); - - const initialData = useMemo(() => { - if (!post) return {}; - return { - title: post.title, - content: post.content, - excerpt: post.excerpt, - slug: post.slug, - published: post.published, - image: post.image || "", - tags: post.tags.map((tag) => ({ - id: tag.id, - name: tag.name, - slug: tag.slug, - })), - }; - }, [post]); + const notify = useNotify(); const schema = CustomPostUpdateSchema; - const { - mutateAsync: updatePost, - isPending: isUpdatingPost, - error: updatePostError, - } = useUpdatePost(); - - const { mutateAsync: deletePost, isPending: isDeletingPost } = - useDeletePost(); - type EditPostFormValues = z.input; - const onSubmit = async (data: EditPostFormValues) => { - // Wait for mutation to complete, including refresh - const updatedPost = await updatePost({ - id: post!.id, + + const resourceForm = blog.posts.useForm< + EditPostFormValues, + SerializedPost | null + >({ + action: "edit", + // Record comes from the suspense hook above — skips useForm's own fetch + record: post, + successMessage: localization.BLOG_FORMS_TOAST_UPDATE_SUCCESS, + defaults: (record) => + (record + ? { + title: record.title, + content: record.content, + excerpt: record.excerpt, + slug: record.slug, + published: record.published, + image: record.image || "", + tags: record.tags.map((tag) => ({ + id: tag.id, + name: tag.name, + slug: tag.slug, + })), + } + : {}) as EditPostFormValues, + toUpdateVars: (data, record) => ({ + id: (record as SerializedPost).id, data: { - id: post!.id, + id: (record as SerializedPost).id, title: data.title, content: data.content, excerpt: data.excerpt ?? "", slug: data.slug, published: data.published ?? false, publishedAt: - data.published && !post?.published + data.published && !record?.published ? new Date() - : post?.publishedAt - ? new Date(post.publishedAt) + : record?.publishedAt + ? new Date(record.publishedAt) : undefined, image: data.image, tags: data.tags || [], }, - }); + }), + onSuccess: (updatedPost) => { + // Navigate only after mutation (including invalidation) completes + onSuccess({ + slug: updatedPost?.slug ?? "", + published: updatedPost?.published ?? false, + }); + }, + }); - toast.success(localization.BLOG_FORMS_TOAST_UPDATE_SUCCESS); + const { mutateAsync: deletePost, isPending: isDeletingPost } = + useDeletePost(); - // Navigate only after mutation completes - onSuccess({ - slug: updatedPost?.slug ?? "", - published: updatedPost?.published ?? false, - }); + const onSubmit = async (data: EditPostFormValues) => { + await resourceForm.submit(data); }; const handleDelete = async () => { if (!post?.id) return; - await deletePost({ id: post.id }); - toast.success(localization.BLOG_FORMS_TOAST_DELETE_SUCCESS); + try { + await deletePost({ id: post.id }); + } catch (error) { + notify.error( + error instanceof Error + ? error.message + : localization.BLOG_FORMS_TOAST_DELETE_FAILURE, + ); + return; + } + notify.success(localization.BLOG_FORMS_TOAST_DELETE_SUCCESS); setDeleteDialogOpen(false); // Call onDelete callback if provided, otherwise use onClose @@ -555,9 +587,13 @@ const EditPostFormComponent = ({ image: "", tags: [], }, - values: initialData as z.input, + values: resourceForm.defaultValues as z.input, }); + // Server-side Zod validation failures land on the matching form fields + useServerFieldErrors(form, resourceForm.fieldErrors); + const hasFieldErrors = Object.keys(resourceForm.fieldErrors).length > 0; + // Expose form instance to parent for AI context integration useEffect(() => { onFormReady?.(form); @@ -574,13 +610,13 @@ const EditPostFormComponent = ({ form={form} onSubmit={onSubmit} submitLabel={ - isUpdatingPost + resourceForm.isSubmitting ? localization.BLOG_FORMS_SUBMIT_UPDATE_PENDING : localization.BLOG_FORMS_SUBMIT_UPDATE_IDLE } onCancel={onClose} - disabled={isUpdatingPost || featuredImageUploading} - errorMessage={updatePostError?.message} + disabled={resourceForm.isSubmitting || featuredImageUploading} + errorMessage={hasFieldErrors ? undefined : resourceForm.error?.message} setFeaturedImageUploading={setFeaturedImageUploading} initialSlugTouched={!!post?.slug} /> @@ -591,7 +627,9 @@ const EditPostFormComponent = ({ variant="destructive" type="button" disabled={ - isUpdatingPost || featuredImageUploading || isDeletingPost + resourceForm.isSubmitting || + featuredImageUploading || + isDeletingPost } className="mt-4" > diff --git a/packages/stack/src/plugins/blog/client/hooks/blog-hooks.tsx b/packages/stack/src/plugins/blog/client/hooks/blog-hooks.tsx index eb8881a7e..ceb2f5ae8 100644 --- a/packages/stack/src/plugins/blog/client/hooks/blog-hooks.tsx +++ b/packages/stack/src/plugins/blog/client/hooks/blog-hooks.tsx @@ -1,37 +1,11 @@ "use client"; -import { createApiClient } from "@btst/stack/plugins/client"; -import { - useInfiniteQuery, - useMutation, - useQuery, - useQueryClient, - useSuspenseInfiniteQuery, - useSuspenseQuery, - type InfiniteData, -} from "@tanstack/react-query"; -import type { SerializedPost, SerializedTag } from "../../types"; -import type { BlogApiRouter } from "../../api/plugin"; -import { useDebounce } from "./use-debounce"; import { useEffect, useRef } from "react"; import { z } from "zod"; -import { createPostSchema, updatePostSchema } from "../../schemas"; -import { createBlogQueryKeys } from "../../query-keys"; -import { usePluginOverrides } from "@btst/stack/context"; -import type { BlogPluginOverrides } from "../overrides"; - -/** - * Shared React Query configuration for all blog queries - * Prevents automatic refetching to avoid hydration mismatches in SSR - */ -const SHARED_QUERY_CONFIG = { - retry: false, - refetchOnWindowFocus: false, - refetchOnMount: false, - refetchOnReconnect: false, - staleTime: 1000 * 60 * 5, // 5 minutes - gcTime: 1000 * 60 * 10, // 10 minutes -} as const; +import type { SerializedPost, SerializedTag } from "../../types"; +import type { createPostSchema, updatePostSchema } from "../../schemas"; +import { useDebounce } from "./use-debounce"; +import { blog } from "./blog-resource"; /** * Options for the usePosts hook @@ -132,31 +106,7 @@ export type PostUpdateInput = z.infer; * Hook for fetching paginated posts with load more functionality */ export function usePosts(options: UsePostsOptions = {}): UsePostsResult { - const { apiBaseURL, apiBasePath, headers } = - usePluginOverrides("blog"); - const client = createApiClient({ - baseURL: apiBaseURL, - basePath: apiBasePath, - }); - const { - tag, - tagSlug, - limit = 10, - enabled = true, - query, - published, - } = options; - const queries = createBlogQueryKeys(client, headers); - - const queryParams = { - tag, - tagSlug, - limit, - query, - published, - }; - - const basePosts = queries.posts.list(queryParams); + const { tagSlug, limit = 10, enabled = true, query, published } = options; const { data, @@ -166,21 +116,11 @@ export function usePosts(options: UsePostsOptions = {}): UsePostsResult { hasNextPage, isFetchingNextPage, refetch, - } = useInfiniteQuery({ - ...basePosts, - ...SHARED_QUERY_CONFIG, - initialPageParam: 0, - getNextPageParam: (lastPage, allPages) => { - const posts = lastPage as SerializedPost[]; - if (posts.length < limit) return undefined; - return allPages.length * limit; - }, - enabled: enabled && !!client, + } = blog.posts.list.useInfinite([{ tagSlug, limit, query, published }], { + enabled, }); - const posts = (( - data as InfiniteData | undefined - )?.pages?.flat() ?? []) as SerializedPost[]; + const posts = data?.pages?.flat() ?? []; return { posts, @@ -201,51 +141,12 @@ export function useSuspensePosts(options: UsePostsOptions = {}): { isLoadingMore: boolean; refetch: () => Promise; } { - const { apiBaseURL, apiBasePath, headers } = - usePluginOverrides("blog"); - const client = createApiClient({ - baseURL: apiBaseURL, - basePath: apiBasePath, - }); - const { - tag, - tagSlug, - limit = 10, - enabled = true, - query, - published, - } = options; - const queries = createBlogQueryKeys(client, headers); - - const queryParams = { tag, tagSlug, limit, query, published }; - const basePosts = queries.posts.list(queryParams); - - const { - data, - fetchNextPage, - hasNextPage, - isFetchingNextPage, - refetch, - error, - isFetching, - } = useSuspenseInfiniteQuery({ - ...basePosts, - ...SHARED_QUERY_CONFIG, - initialPageParam: 0, - getNextPageParam: (lastPage, allPages) => { - const posts = lastPage as SerializedPost[]; - if (posts.length < limit) return undefined; - return allPages.length * limit; - }, - }); + const { tagSlug, limit = 10, query, published } = options; - // Manually throw errors for Error Boundaries (per React Query Suspense docs) - // useSuspenseQuery only throws errors if there's no data, but we want to throw always - if (error && !isFetching) { - throw error; - } + const { data, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } = + blog.posts.list.useSuspenseInfinite([{ tagSlug, limit, query, published }]); - const posts = (data.pages?.flat() ?? []) as SerializedPost[]; + const posts = data.pages?.flat() ?? []; return { posts, @@ -260,25 +161,10 @@ export function useSuspensePosts(options: UsePostsOptions = {}): { * Hook for fetching a single post by slug */ export function usePost(slug?: string): UsePostResult { - const { apiBaseURL, apiBasePath, headers } = - usePluginOverrides("blog"); - const client = createApiClient({ - baseURL: apiBaseURL, - basePath: apiBasePath, - }); - const queries = createBlogQueryKeys(client, headers); - - const basePost = queries.posts.detail(slug ?? ""); - const { data, isLoading, error, refetch } = useQuery< - SerializedPost | null, - Error, - SerializedPost | null, - typeof basePost.queryKey - >({ - ...basePost, - ...SHARED_QUERY_CONFIG, - enabled: !!client && !!slug, - }); + const { data, isLoading, error, refetch } = blog.posts.detail.use( + [slug ?? ""], + { enabled: !!slug }, + ); return { post: data || null, @@ -293,29 +179,7 @@ export function useSuspensePost(slug: string): { post: SerializedPost | null; refetch: () => Promise; } { - const { apiBaseURL, apiBasePath, headers } = - usePluginOverrides("blog"); - const client = createApiClient({ - baseURL: apiBaseURL, - basePath: apiBasePath, - }); - const queries = createBlogQueryKeys(client, headers); - const basePost = queries.posts.detail(slug); - const { data, refetch, error, isFetching } = useSuspenseQuery< - SerializedPost | null, - Error, - SerializedPost | null, - typeof basePost.queryKey - >({ - ...basePost, - ...SHARED_QUERY_CONFIG, - }); - - // Manually throw errors for Error Boundaries (per React Query Suspense docs) - // useSuspenseQuery only throws errors if there's no data, but we want to throw always - if (error && !isFetching) { - throw error; - } + const { data, refetch } = blog.posts.detail.useSuspense([slug]); return { post: data || null, refetch }; } @@ -329,24 +193,7 @@ export function useTags(): { error: Error | null; refetch: () => void; } { - const { apiBaseURL, apiBasePath, headers } = - usePluginOverrides("blog"); - const client = createApiClient({ - baseURL: apiBaseURL, - basePath: apiBasePath, - }); - const queries = createBlogQueryKeys(client, headers); - const baseTags = queries.tags.list(); - const { data, isLoading, error, refetch } = useQuery< - SerializedTag[] | null, - Error, - SerializedTag[] | null, - typeof baseTags.queryKey - >({ - ...baseTags, - ...SHARED_QUERY_CONFIG, - enabled: !!client, - }); + const { data, isLoading, error, refetch } = blog.tags.list.use([]); return { tags: data ?? [], @@ -361,29 +208,7 @@ export function useSuspenseTags(): { tags: SerializedTag[]; refetch: () => Promise; } { - const { apiBaseURL, apiBasePath, headers } = - usePluginOverrides("blog"); - const client = createApiClient({ - baseURL: apiBaseURL, - basePath: apiBasePath, - }); - const queries = createBlogQueryKeys(client, headers); - const baseTags = queries.tags.list(); - const { data, refetch, error, isFetching } = useSuspenseQuery< - SerializedTag[] | null, - Error, - SerializedTag[] | null, - typeof baseTags.queryKey - >({ - ...baseTags, - ...SHARED_QUERY_CONFIG, - }); - - // Manually throw errors for Error Boundaries (per React Query Suspense docs) - // useSuspenseQuery only throws errors if there's no data, but we want to throw always - if (error && !isFetching) { - throw error; - } + const { data, refetch } = blog.tags.list.useSuspense([]); return { tags: data ?? [], @@ -393,133 +218,17 @@ export function useSuspenseTags(): { /** Create a new post */ export function useCreatePost() { - const { refresh, apiBaseURL, apiBasePath } = - usePluginOverrides("blog"); - const client = createApiClient({ - baseURL: apiBaseURL, - basePath: apiBasePath, - }); - const queryClient = useQueryClient(); - const queries = createBlogQueryKeys(client); - - return useMutation({ - mutationKey: [...queries.posts._def, "create"], - mutationFn: async (postData: PostCreateInput) => { - const response = await client("@post/posts", { - method: "POST", - body: postData, - }); - return response.data as SerializedPost | null; - }, - onSuccess: async (created) => { - // Update detail cache if available - if (created?.slug) { - queryClient.setQueryData( - queries.posts.detail(created.slug).queryKey, - created, - ); - } - // Invalidate lists scoped to posts and drafts - wait for completion - await queryClient.invalidateQueries({ - queryKey: queries.posts.list._def, - }); - await queryClient.invalidateQueries({ - queryKey: queries.drafts.list._def, - }); - // Refresh server-side cache (Next.js router cache) - if (refresh) { - await refresh(); - } - }, - }); + return blog.posts.create.use(); } /** Update an existing post by id */ export function useUpdatePost() { - const { refresh, apiBaseURL, apiBasePath } = - usePluginOverrides("blog"); - - const client = createApiClient({ - baseURL: apiBaseURL, - basePath: apiBasePath, - }); - - const queryClient = useQueryClient(); - const queries = createBlogQueryKeys(client); - - return useMutation< - SerializedPost | null, - Error, - { id: string; data: PostUpdateInput } - >({ - mutationKey: [...queries.posts._def, "update"], - mutationFn: async ({ id, data }: { id: string; data: PostUpdateInput }) => { - const response = await client(`@put/posts/:id`, { - method: "PUT", - params: { id }, - body: data, - }); - return response.data as SerializedPost | null; - }, - onSuccess: async (updated) => { - // Update detail cache if available - if (updated?.slug) { - queryClient.setQueryData( - queries.posts.detail(updated.slug).queryKey, - updated, - ); - } - // Invalidate lists scoped to posts and drafts - wait for completion - await queryClient.invalidateQueries({ - queryKey: queries.posts.list._def, - }); - await queryClient.invalidateQueries({ - queryKey: queries.drafts.list._def, - }); - // Refresh server-side cache (Next.js router cache) - if (refresh) { - await refresh(); - } - }, - }); + return blog.posts.update.use(); } /** Delete a post by id */ export function useDeletePost() { - const { refresh, apiBaseURL, apiBasePath } = - usePluginOverrides("blog"); - - const client = createApiClient({ - baseURL: apiBaseURL, - basePath: apiBasePath, - }); - - const queryClient = useQueryClient(); - const queries = createBlogQueryKeys(client); - - return useMutation<{ success: boolean }, Error, { id: string }>({ - mutationKey: [...queries.posts._def, "delete"], - mutationFn: async ({ id }: { id: string }) => { - const response = await client(`@delete/posts/:id`, { - method: "DELETE", - params: { id }, - }); - return response.data as { success: boolean }; - }, - onSuccess: async () => { - // Invalidate all post lists and detail caches - wait for completion - await queryClient.invalidateQueries({ - queryKey: queries.posts._def, - }); - await queryClient.invalidateQueries({ - queryKey: queries.drafts.list._def, - }); - // Refresh server-side cache (Next.js router cache) - if (refresh) { - await refresh(); - } - }, - }); + return blog.posts.delete.use(); } /** @@ -610,28 +319,13 @@ export function useNextPreviousPosts( createdAt: string | Date, options: UseNextPreviousPostsOptions = {}, ): UseNextPreviousPostsResult { - const { apiBaseURL, apiBasePath, headers } = - usePluginOverrides("blog"); - const client = createApiClient({ - baseURL: apiBaseURL, - basePath: apiBasePath, - }); - const queries = createBlogQueryKeys(client, headers); - const dateValue = typeof createdAt === "string" ? new Date(createdAt) : createdAt; - const baseQuery = queries.posts.nextPrevious(dateValue); - - const { data, isLoading, error, refetch } = useQuery< - { previous: SerializedPost | null; next: SerializedPost | null }, - Error, - { previous: SerializedPost | null; next: SerializedPost | null }, - typeof baseQuery.queryKey - >({ - ...baseQuery, - ...SHARED_QUERY_CONFIG, - enabled: (options.enabled ?? true) && !!client, - }); + + const { data, isLoading, error, refetch } = blog.posts.nextPrevious.use( + [dateValue], + { enabled: options.enabled ?? true }, + ); return { previousPost: data?.previous ?? null, @@ -675,29 +369,10 @@ export interface UseRecentPostsResult { export function useRecentPosts( options: UseRecentPostsOptions = {}, ): UseRecentPostsResult { - const { apiBaseURL, apiBasePath, headers } = - usePluginOverrides("blog"); - const client = createApiClient({ - baseURL: apiBaseURL, - basePath: apiBasePath, - }); - const queries = createBlogQueryKeys(client, headers); - - const baseQuery = queries.posts.recent({ - limit: options.limit ?? 5, - excludeSlug: options.excludeSlug, - }); - - const { data, isLoading, error, refetch } = useQuery< - SerializedPost[], - Error, - SerializedPost[], - typeof baseQuery.queryKey - >({ - ...baseQuery, - ...SHARED_QUERY_CONFIG, - enabled: (options.enabled ?? true) && !!client, - }); + const { data, isLoading, error, refetch } = blog.posts.recent.use( + [{ limit: options.limit ?? 5, excludeSlug: options.excludeSlug }], + { enabled: options.enabled ?? true }, + ); return { recentPosts: data ?? [], diff --git a/packages/stack/src/plugins/blog/client/hooks/blog-resource.ts b/packages/stack/src/plugins/blog/client/hooks/blog-resource.ts new file mode 100644 index 000000000..15dc6584b --- /dev/null +++ b/packages/stack/src/plugins/blog/client/hooks/blog-resource.ts @@ -0,0 +1,13 @@ +"use client"; + +import { createResource } from "@btst/stack/plugins/client/hooks"; +import { blogResources } from "../../query-keys"; + +/** + * Factory-generated blog resource hooks. Internal — the public hook surface + * (`usePosts`, `useSuspensePost`, ...) in `blog-hooks.tsx` wraps these. + */ +export const blog = createResource({ + plugin: "blog", + resources: blogResources, +}); diff --git a/packages/stack/src/plugins/blog/client/localization/blog-forms.ts b/packages/stack/src/plugins/blog/client/localization/blog-forms.ts index 46191a232..f49a7dee2 100644 --- a/packages/stack/src/plugins/blog/client/localization/blog-forms.ts +++ b/packages/stack/src/plugins/blog/client/localization/blog-forms.ts @@ -26,6 +26,7 @@ export const BLOG_FORMS = { BLOG_FORMS_TOAST_CREATE_SUCCESS: "Post created successfully", BLOG_FORMS_TOAST_UPDATE_SUCCESS: "Post updated successfully", BLOG_FORMS_TOAST_DELETE_SUCCESS: "Post deleted successfully", + BLOG_FORMS_TOAST_DELETE_FAILURE: "Failed to delete post", BLOG_FORMS_LOADING_POST: "Loading post...", // Delete post diff --git a/packages/stack/src/plugins/blog/query-keys.ts b/packages/stack/src/plugins/blog/query-keys.ts index f232660eb..e4b9d1b1c 100644 --- a/packages/stack/src/plugins/blog/query-keys.ts +++ b/packages/stack/src/plugins/blog/query-keys.ts @@ -1,9 +1,11 @@ -import { - mergeQueryKeys, - createQueryKeys, -} from "@lukemorales/query-key-factory"; import type { BlogApiRouter } from "./api"; -import { createApiClient } from "@btst/stack/plugins/client"; +import { + createApiClient, + createResourceQueryKeys, + type ResourcesDeclaration, +} from "@btst/stack/plugins/client"; +import type { z } from "zod"; +import type { createPostSchema, updatePostSchema } from "./schemas"; import type { SerializedPost, SerializedTag } from "./types"; import { postsListDiscriminator } from "./api/query-key-defs"; @@ -14,265 +16,172 @@ interface PostsListParams { tagSlug?: string; } -// Type guard for better-call error responses -// better-call client returns Error$1 | Data -// We check if error exists and is not null/undefined to determine it's an error response -function isErrorResponse( - response: unknown, -): response is { error: unknown; data?: never } { - return ( - typeof response === "object" && - response !== null && - "error" in response && - response.error !== null && - response.error !== undefined - ); -} - -// Helper to convert error to a proper Error object with meaningful message -function toError(error: unknown): Error { - if (error instanceof Error) { - return error; - } - - // Handle object errors (likely from better-call APIError) - if (typeof error === "object" && error !== null) { - // Try to extract message from common error object structures - const errorObj = error as Record; - const message = - (typeof errorObj.message === "string" ? errorObj.message : null) || - (typeof errorObj.error === "string" ? errorObj.error : null) || - JSON.stringify(error); - - const err = new Error(message); - // Preserve other properties - Object.assign(err, error); - return err; - } - - // Fallback for primitive values - return new Error(String(error)); -} - -export function createBlogQueryKeys( - client: ReturnType>, - headers?: HeadersInit, -) { - const posts = createPostsQueries(client, headers); - const drafts = createDraftsQueries(client, headers); - const tags = createTagsQueries(client, headers); - - return mergeQueryKeys(posts, drafts, tags); -} - -function createPostsQueries( - client: ReturnType>, - headers?: HeadersInit, -) { - return createQueryKeys("posts", { - list: (params?: PostsListParams) => ({ - queryKey: [ - postsListDiscriminator({ - published: params?.published ?? true, +type PostCreateInput = z.infer; +type PostUpdateInput = z.infer; + +/** + * Blog resource declaration — the single source of truth for query keys, + * HTTP mappings and mutations. Feeds both `createBlogQueryKeys` (SSR + * loaders) and `createResource` (client hooks, see `client/hooks`). + * + * Key shapes intentionally match `BLOG_QUERY_KEYS` in + * `api/query-key-defs.ts` so SSG `prefetchForRoute` hydration keeps working. + */ +export const blogResources = { + posts: { + queries: { + list: { + path: "/posts", + query: (params?: PostsListParams) => ({ + query: params?.query, limit: params?.limit ?? 10, + published: + params?.published !== undefined + ? params.published + ? "true" + : "false" + : undefined, tagSlug: params?.tagSlug, - query: params?.query, }), - ], - queryFn: async ({ pageParam }: { pageParam?: number }) => { - try { - const response = await client("/posts", { - method: "GET", - query: { - query: params?.query, - offset: pageParam ?? 0, - limit: params?.limit ?? 10, - published: - params?.published !== undefined - ? params.published - ? "true" - : "false" - : undefined, - tagSlug: params?.tagSlug, - }, - headers, - }); - // Check for errors (better-call returns Error$1 | Data) - if (isErrorResponse(response)) { - const errorResponse = response as { error: unknown }; - throw toError(errorResponse.error); - } - // Extract .items from the paginated response for infinite scroll compatibility - const dataResponse = response as { data?: { items?: unknown[] } }; - return (dataResponse.data?.items ?? - []) as unknown as SerializedPost[]; - } catch (error) { - // Re-throw errors so React Query can catch them - throw error; - } + key: (params?: PostsListParams) => [ + postsListDiscriminator({ + published: params?.published ?? true, + limit: params?.limit ?? 10, + tagSlug: params?.tagSlug, + query: params?.query, + }), + ], + select: (data: any, _params?: PostsListParams): SerializedPost[] => + data?.items ?? [], + infinite: true, + pageSize: (params?: PostsListParams) => params?.limit ?? 10, }, - }), - // Simplified detail query - detail: (slug: string) => ({ - queryKey: [slug], - queryFn: async () => { - if (!slug) return null; - - try { - const response = await client("/posts", { - method: "GET", - query: { slug, limit: 1 }, - headers, - }); - // Check for errors (better-call returns Error$1 | Data) - if (isErrorResponse(response)) { - const errorResponse = response as { error: unknown }; - throw toError(errorResponse.error); - } - // Type narrowed to Data after error check — access .items[0] - const dataResponse = response as { data?: { items?: unknown[] } }; - return (dataResponse.data?.items?.[0] ?? - null) as unknown as SerializedPost | null; - } catch (error) { - // Re-throw errors so React Query can catch them - throw error; - } + detail: { + path: "/posts", + query: (slug: string) => ({ slug, limit: 1 }), + key: (slug: string) => [slug], + select: (data: any, _slug: string): SerializedPost | null => + data?.items?.[0] ?? null, + skip: (slug: string) => !slug, }, - }), - // Next/previous posts query - nextPrevious: (date: Date | string) => ({ - queryKey: ["nextPrevious", date], - queryFn: async () => { - const dateValue = typeof date === "string" ? new Date(date) : date; - const response = await client("/posts/next-previous", { - method: "GET", - query: { - date: dateValue.toISOString(), - }, - headers, - }); - // Check for errors (better-call returns Error$1 | Data<...>) - if (isErrorResponse(response)) { - const errorResponse = response as { error: unknown }; - throw toError(errorResponse.error); - } - // Type narrowed to Data<...> after error check - const dataResponse = response as { data?: unknown }; - return dataResponse.data as { + nextPrevious: { + path: "/posts/next-previous", + query: (date: Date | string) => ({ + date: (typeof date === "string" + ? new Date(date) + : date + ).toISOString(), + }), + key: (date: Date | string) => ["nextPrevious", date], + select: ( + data: any, + _date: Date | string, + ): { previous: SerializedPost | null; next: SerializedPost | null; - }; + } => data, }, - }), - - // Recent posts query (separate from main list to avoid cache conflicts) - recent: (params?: { limit?: number; excludeSlug?: string }) => ({ - queryKey: ["recent", params], - queryFn: async () => { - try { - const response = await client("/posts", { - method: "GET", - query: { - limit: params?.limit ?? 5, - published: "true", - }, - headers, - }); - // Check for errors (better-call returns Error$1 | Data) - if (isErrorResponse(response)) { - const errorResponse = response as { error: unknown }; - throw toError(errorResponse.error); - } - // Extract .items from the paginated response - const recentResponse = response as { data?: { items?: unknown[] } }; - let posts = (recentResponse.data?.items ?? - []) as unknown as SerializedPost[]; - - // Exclude current post if specified - if (params?.excludeSlug) { - posts = posts.filter((post) => post.slug !== params.excludeSlug); - } - return posts; - } catch (error) { - // Re-throw errors so React Query can catch them - throw error; - } + // Recent posts query (separate from the main list to avoid cache conflicts) + recent: { + path: "/posts", + query: (params?: { limit?: number; excludeSlug?: string }) => ({ + limit: params?.limit ?? 5, + published: "true", + }), + key: (params?: { limit?: number; excludeSlug?: string }) => [ + "recent", + params, + ], + select: ( + data: any, + params?: { limit?: number; excludeSlug?: string }, + ): SerializedPost[] => { + const posts: SerializedPost[] = data?.items ?? []; + return params?.excludeSlug + ? posts.filter((post) => post.slug !== params.excludeSlug) + : posts; + }, }, - }), - }); -} - -function createDraftsQueries( - client: ReturnType>, - headers?: HeadersInit, -) { - return createQueryKeys("drafts", { - list: (params?: PostsListParams) => ({ - queryKey: [ - { - ...(params?.limit && { limit: params.limit }), + }, + + mutations: { + create: { + path: "@post/posts", + method: "POST" as const, + input: (vars: PostCreateInput) => ({ body: vars }), + select: (data: any) => data as SerializedPost | null, + invalidates: ["posts.list", "drafts.list"], + setData: { + query: "detail", + args: (created: SerializedPost | null) => + created?.slug ? [created.slug] : null, }, - ], - queryFn: async ({ pageParam }: { pageParam?: number }) => { - try { - const response = await client("/posts", { - method: "GET", - query: { - query: params?.query, - offset: pageParam ?? 0, - limit: params?.limit ?? 10, - published: "false", - }, - headers, - }); - // Check for errors (better-call returns Error$1 | Data) - if (isErrorResponse(response)) { - const errorResponse = response as { error: unknown }; - throw toError(errorResponse.error); - } - // Extract .items from the paginated response for infinite scroll compatibility - const draftsResponse = response as { data?: { items?: unknown[] } }; - return (draftsResponse.data?.items ?? - []) as unknown as SerializedPost[]; - } catch (error) { - // Re-throw errors so React Query can catch them - throw error; - } }, - }), - }); -} + update: { + path: "@put/posts/:id", + method: "PUT" as const, + input: (vars: { id: string; data: PostUpdateInput }) => ({ + params: { id: vars.id }, + body: vars.data, + }), + select: (data: any) => data as SerializedPost | null, + invalidates: ["posts.list", "drafts.list"], + setData: { + query: "detail", + args: (updated: SerializedPost | null) => + updated?.slug ? [updated.slug] : null, + }, + }, + delete: { + path: "@delete/posts/:id", + method: "DELETE" as const, + input: (vars: { id: string }) => ({ params: { id: vars.id } }), + select: (data: any) => data as { success: boolean }, + invalidates: ["posts", "drafts.list"], + }, + }, + }, + + drafts: { + queries: { + list: { + path: "/posts", + query: (params?: PostsListParams) => ({ + query: params?.query, + limit: params?.limit ?? 10, + published: "false", + }), + key: (params?: PostsListParams) => [ + { + ...(params?.limit && { limit: params.limit }), + }, + ], + select: (data: any, _params?: PostsListParams): SerializedPost[] => + data?.items ?? [], + infinite: true, + pageSize: (params?: PostsListParams) => params?.limit ?? 10, + }, + }, + }, + + tags: { + queries: { + list: { + path: "/tags", + key: () => ["tags"], + // The API returns serialized tags (dates as strings) + select: (data: any): SerializedTag[] => data ?? [], + }, + }, + }, +} satisfies ResourcesDeclaration; -function createTagsQueries( +export function createBlogQueryKeys( client: ReturnType>, headers?: HeadersInit, ) { - return createQueryKeys("tags", { - list: () => ({ - queryKey: ["tags"], - queryFn: async () => { - try { - const response = await client("/tags", { - method: "GET", - headers, - }); - // Check for errors (better-call returns Error$1 | Data) - if (isErrorResponse(response)) { - const errorResponse = response as { error: unknown }; - throw toError(errorResponse.error); - } - // Type narrowed to Data after error check - // The API returns serialized tags (dates as strings) - return ((response as { data?: unknown }).data ?? - []) as unknown as SerializedTag[]; - } catch (error) { - // Re-throw errors so React Query can catch them - throw error; - } - }, - }), - }); + return createResourceQueryKeys(client, blogResources, headers); } diff --git a/packages/stack/src/plugins/client/hooks/index.tsx b/packages/stack/src/plugins/client/hooks/index.tsx new file mode 100644 index 000000000..12a50dce9 --- /dev/null +++ b/packages/stack/src/plugins/client/hooks/index.tsx @@ -0,0 +1,32 @@ +/** + * Client-only resource hooks (`"use client"` modules). + * + * Kept separate from `@btst/stack/plugins/client` so plugin factory entries + * (`client/plugin.tsx`) stay server-import-safe during SSG. + */ + +export { + createResource, + type CreateResourceConfig, + type Resource, + type ResourceDetailData, + type ResourceHandle, + type ResourceInfiniteQueryHooks, + type ResourceMutationHooks, + type ResourcePlainQueryHooks, + type ResourceQueryHooks, + type ResourceQueryOptions, +} from "../resource/hooks"; +export type { ResourceOverrides } from "../resource/internal"; +export { + createUseForm, + type ResourceFormConfig, + type ResourceFormResult, +} from "../resource/use-form"; +export { + createUseSelect, + type ResourceSelectConfig, + type ResourceSelectOption, + type ResourceSelectResult, +} from "../resource/use-select"; +export { useDebounce } from "../resource/use-debounce"; diff --git a/packages/stack/src/plugins/client/index.ts b/packages/stack/src/plugins/client/index.ts index 775a5bfff..12be1d95e 100644 --- a/packages/stack/src/plugins/client/index.ts +++ b/packages/stack/src/plugins/client/index.ts @@ -26,6 +26,36 @@ export { SSR_LOADER_ERROR_MESSAGE, } from "../utils"; +// Shared error contract + React Query config for data plugins +export { + isErrorResponse, + SHARED_QUERY_CONFIG, + toError, +} from "./resource/errors"; +export type { StackError } from "./resource/errors"; + +// Resource declaration types + server-safe query-key factory +export { + buildQueryKey, + createResourceQueryKeys, + resolvePageSize, + runResourceMutation, + runResourceQuery, +} from "./resource/queries"; +export type { + ResourceClient, + ResourceDef, + ResourceMutationDef, + ResourceMutationResult, + ResourceMutationVars, + ResourceQueryArgs, + ResourceQueryData, + ResourceQueryDef, + ResourceQueryEntry, + ResourceQueryKeys, + ResourcesDeclaration, +} from "./resource/queries"; + // Re-export Yar types needed for plugins export type { Route, RouteContext, RouteDef } from "@btst/yar"; export { diff --git a/packages/stack/src/plugins/client/resource/errors.ts b/packages/stack/src/plugins/client/resource/errors.ts new file mode 100644 index 000000000..8510b34de --- /dev/null +++ b/packages/stack/src/plugins/client/resource/errors.ts @@ -0,0 +1,160 @@ +/** + * Shared error contract and React Query config for all data plugins. + * + * Every plugin used to copy-paste `isErrorResponse` / `toError` / + * `SHARED_QUERY_CONFIG` into its own `query-keys.ts` / hooks files. These now + * live in core and are exported once from `@btst/stack/plugins/client`. + */ + +/** + * Standardized error shape thrown by resource queries and mutations. + * + * `errors` maps field names to validation message(s), preserving Zod + * field-level issues from better-call endpoint validation errors so form + * hooks can map them onto per-field error state. + */ +export interface StackError extends Error { + statusCode?: number; + errors?: Record; +} + +/** + * Shared React Query configuration for all plugin queries. + * Prevents automatic refetching to avoid hydration mismatches in SSR. + */ +export const SHARED_QUERY_CONFIG = { + retry: false, + refetchOnWindowFocus: false, + refetchOnMount: false, + refetchOnReconnect: false, + staleTime: 1000 * 60 * 5, // 5 minutes + gcTime: 1000 * 60 * 10, // 10 minutes +} as const; + +/** + * Type guard for better-call error responses. + * better-call client returns `Error$1 | Data` — we check if + * `error` exists and is not null/undefined to determine it's an error response. + */ +export function isErrorResponse( + response: unknown, +): response is { error: unknown; data?: never } { + return ( + typeof response === "object" && + response !== null && + "error" in response && + response.error !== null && + response.error !== undefined + ); +} + +/** Serialized validation issue shape included in better-call 400 responses. */ +interface SerializedIssue { + path?: Array; + message?: string; +} + +function issuePathToFieldName(issue: SerializedIssue): string { + const path = Array.isArray(issue.path) ? issue.path : []; + return path + .map((segment) => + typeof segment === "object" && segment !== null + ? String(segment.key) + : String(segment), + ) + .join("."); +} + +/** + * Converts a validation `issues` array into a field-name → message(s) map. + * Issues without a path are skipped — they only contribute to the top-level + * error message. + */ +function issuesToFieldErrors( + issues: unknown, +): Record | undefined { + if (!Array.isArray(issues) || issues.length === 0) return undefined; + + const fieldErrors: Record = {}; + for (const issue of issues as SerializedIssue[]) { + if (typeof issue !== "object" || issue === null) continue; + const field = issuePathToFieldName(issue); + if (!field || typeof issue.message !== "string") continue; + (fieldErrors[field] ??= []).push(issue.message); + } + + const entries = Object.entries(fieldErrors); + if (entries.length === 0) return undefined; + + return Object.fromEntries( + entries.map(([field, messages]) => [ + field, + messages.length === 1 ? messages[0]! : messages, + ]), + ); +} + +function isFieldErrorRecord( + value: unknown, +): value is Record { + if (typeof value !== "object" || value === null || Array.isArray(value)) { + return false; + } + return Object.values(value).every( + (v) => + typeof v === "string" || + (Array.isArray(v) && v.every((m) => typeof m === "string")), + ); +} + +/** + * Converts an unknown error (typically a better-call / better-fetch error + * response body) into a proper `StackError` with a meaningful message. + * + * Normalizes to `{ message, statusCode?, errors? }` where `errors` maps + * field names to validation messages (preserved from Zod issues in + * better-call endpoint validation error responses). + */ +export function toError(error: unknown): StackError { + if (error instanceof Error) { + return error as StackError; + } + + // Handle object errors (likely from better-call APIError response bodies) + if (typeof error === "object" && error !== null) { + const errorObj = error as Record; + const message = + (typeof errorObj.message === "string" ? errorObj.message : null) || + (typeof errorObj.error === "string" ? errorObj.error : null) || + JSON.stringify(error); + + const err = new Error(message) as StackError; + // Preserve other properties (status, code, etc.) + Object.assign(err, error); + + const statusCode = + typeof errorObj.statusCode === "number" + ? errorObj.statusCode + : typeof errorObj.status === "number" + ? errorObj.status + : undefined; + if (statusCode !== undefined) { + err.statusCode = statusCode; + } + + const fieldErrors = isFieldErrorRecord(errorObj.errors) + ? errorObj.errors + : issuesToFieldErrors(errorObj.issues); + if (fieldErrors) { + err.errors = fieldErrors; + } else { + // Object.assign may have copied a non-conforming `errors` property + err.errors = undefined; + } + + return err; + } + + // Fallback for primitive values + return new Error(String(error)); +} diff --git a/packages/stack/src/plugins/client/resource/hooks.tsx b/packages/stack/src/plugins/client/resource/hooks.tsx new file mode 100644 index 000000000..ee2f7977a --- /dev/null +++ b/packages/stack/src/plugins/client/resource/hooks.tsx @@ -0,0 +1,282 @@ +"use client"; + +/** + * `createResource` — generates the repetitive React Query plumbing for a + * plugin's resources from a single declaration: plain queries, suspense + * queries, infinite queries, mutations with invalidation, plus `useForm` + * and `useSelect` per resource. + * + * The same declaration feeds `createResourceQueryKeys` (see `./queries`), + * so SSR loaders and SSG `prefetchForRoute` keys stay in sync with hooks. + * + * @example + * ```ts + * const blog = createResource({ plugin: "blog", resources: blogResources }); + * + * export const usePost = (slug?: string) => + * blog.posts.detail.use([slug ?? ""], { enabled: !!slug }); + * export const useCreatePost = () => blog.posts.create.use(); + * ``` + */ + +import { + useInfiniteQuery, + useQuery, + useSuspenseInfiniteQuery, + useSuspenseQuery, + type InfiniteData, + type UseInfiniteQueryResult, + type UseMutationResult, + type UseQueryResult, + type UseSuspenseInfiniteQueryResult, + type UseSuspenseQueryResult, +} from "@tanstack/react-query"; +import { SHARED_QUERY_CONFIG } from "./errors"; +import { + buildQueryKey, + resolvePageSize, + runResourceQuery, + type ResourceDef, + type ResourceMutationDef, + type ResourceMutationResult, + type ResourceMutationVars, + type ResourceQueryArgs, + type ResourceQueryData, + type ResourceQueryDef, + type ResourcesDeclaration, +} from "./queries"; +import { useResourceContext, useResourceMutationForDef } from "./internal"; +import { + createUseForm, + type ResourceFormConfig, + type ResourceFormResult, +} from "./use-form"; +import { + createUseSelect, + type ResourceSelectConfig, + type ResourceSelectResult, +} from "./use-select"; + +/** Per-call options for generated query hooks. */ +export interface ResourceQueryOptions { + enabled?: boolean; +} + +/** Hooks generated for a non-infinite query declaration. */ +export interface ResourcePlainQueryHooks< + TArgs extends readonly unknown[], + TData, +> { + use( + args?: TArgs, + options?: ResourceQueryOptions, + ): UseQueryResult; + /** Suspense variant — re-throws refetch errors for Error Boundaries */ + useSuspense(args?: TArgs): UseSuspenseQueryResult; +} + +/** Hooks generated for an infinite query declaration. */ +export interface ResourceInfiniteQueryHooks< + TArgs extends readonly unknown[], + TData, +> { + useInfinite( + args?: TArgs, + options?: ResourceQueryOptions, + ): UseInfiniteQueryResult, Error>; + /** Suspense variant — re-throws refetch errors for Error Boundaries */ + useSuspenseInfinite( + args?: TArgs, + ): UseSuspenseInfiniteQueryResult, Error>; +} + +/** Dispatches to the plain or infinite hook set based on the declaration. */ +export type ResourceQueryHooks = TDef extends { infinite: true } + ? ResourceInfiniteQueryHooks, ResourceQueryData> + : ResourcePlainQueryHooks, ResourceQueryData>; + +/** Hook generated for a mutation declaration. */ +export interface ResourceMutationHooks { + use(): UseMutationResult< + ResourceMutationResult, + Error, + ResourceMutationVars + >; +} + +/** The detail-query record type of a resource (used by `useForm` defaults). */ +export type ResourceDetailData = + TResource["queries"] extends { detail: infer TDetail } + ? ResourceQueryData + : unknown; + +/** The generated handle for a single resource. */ +export type ResourceHandle = { + [Q in keyof TResource["queries"]]: ResourceQueryHooks< + TResource["queries"][Q] + >; +} & { + [M in keyof NonNullable]: ResourceMutationHooks< + NonNullable[M] + >; +} & { + useForm>( + config: ResourceFormConfig, + ): ResourceFormResult; + useSelect( + config: ResourceSelectConfig, + ): ResourceSelectResult; +}; + +/** The generated resource handles, keyed by resource name. */ +export type Resource = { + [R in keyof TResources]: ResourceHandle; +}; + +export interface CreateResourceConfig { + /** Plugin name used to resolve overrides (`usePluginOverrides(plugin)`) */ + plugin: string; + resources: TResources; +} + +function createQueryHooks( + plugin: string, + resourceName: string, + queryName: string, + def: ResourceQueryDef, +) { + const useQueryConfig = (args: readonly unknown[]) => { + const context = useResourceContext(plugin); + return { + queryKey: buildQueryKey(resourceName, queryName, def, args), + queryFn: (queryContext?: { pageParam?: unknown }) => + runResourceQuery( + context.client, + def, + args, + queryContext?.pageParam, + context.headers, + ), + ...SHARED_QUERY_CONFIG, + }; + }; + + const infiniteExtras = (args: readonly unknown[]) => { + const pageSize = resolvePageSize(def, args); + return { + initialPageParam: 0, + getNextPageParam: (lastPage: unknown, allPages: unknown[]) => { + const items = (lastPage as unknown[]) ?? []; + if (items.length < pageSize) return undefined; + return allPages.length * pageSize; + }, + }; + }; + + return { + use(args: readonly unknown[] = [], options?: ResourceQueryOptions) { + return useQuery({ + ...useQueryConfig(args), + ...(options?.enabled !== undefined ? { enabled: options.enabled } : {}), + }); + }, + useSuspense(args: readonly unknown[] = []) { + const result = useSuspenseQuery(useQueryConfig(args)); + // useSuspenseQuery only throws on initial fetch — manually re-throw + // refetch errors so Error Boundaries catch them + if (result.error && !result.isFetching) { + throw result.error; + } + return result; + }, + useInfinite(args: readonly unknown[] = [], options?: ResourceQueryOptions) { + return useInfiniteQuery({ + ...useQueryConfig(args), + ...infiniteExtras(args), + ...(options?.enabled !== undefined ? { enabled: options.enabled } : {}), + }); + }, + useSuspenseInfinite(args: readonly unknown[] = []) { + const result = useSuspenseInfiniteQuery({ + ...useQueryConfig(args), + ...infiniteExtras(args), + }); + if (result.error && !result.isFetching) { + throw result.error; + } + return result; + }, + }; +} + +function createMutationHook( + plugin: string, + resourceName: string, + mutationName: string, + resource: ResourceDef, + def: ResourceMutationDef, +) { + return { + use() { + const context = useResourceContext(plugin); + return useResourceMutationForDef( + context, + resourceName, + mutationName, + resource, + def, + ); + }, + }; +} + +/** + * Generates the full hook surface for a plugin's resources. + * + * Hooks resolve `apiBaseURL` / `apiBasePath` / `headers` / `navigate` / + * `refresh` from `usePluginOverrides(plugin)` at render time, so the + * declaration can live at module scope. + */ +export function createResource( + config: CreateResourceConfig, +): Resource { + const { plugin, resources } = config; + const handles: Record = {}; + + for (const [resourceName, resource] of Object.entries(resources)) { + const handle: Record = {}; + + for (const [queryName, def] of Object.entries(resource.queries)) { + handle[queryName] = createQueryHooks( + plugin, + resourceName, + queryName, + def, + ); + } + + for (const [mutationName, def] of Object.entries( + resource.mutations ?? {}, + )) { + if (handle[mutationName]) { + throw new Error( + `Resource "${resourceName}" declares both a query and a mutation named "${mutationName}"`, + ); + } + handle[mutationName] = createMutationHook( + plugin, + resourceName, + mutationName, + resource, + def, + ); + } + + handle.useForm = createUseForm(plugin, resourceName, resource); + handle.useSelect = createUseSelect(plugin, resourceName, resource); + + handles[resourceName] = handle; + } + + return handles as Resource; +} diff --git a/packages/stack/src/plugins/client/resource/internal.ts b/packages/stack/src/plugins/client/resource/internal.ts new file mode 100644 index 000000000..5b59db0fb --- /dev/null +++ b/packages/stack/src/plugins/client/resource/internal.ts @@ -0,0 +1,110 @@ +"use client"; + +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { UseMutationResult } from "@tanstack/react-query"; +import { usePluginOverrides } from "../../../context"; +import { createApiClient } from "../../utils"; +import { + buildQueryKey, + runResourceMutation, + type ResourceClient, + type ResourceDef, + type ResourceMutationDef, +} from "./queries"; + +/** + * The override fields the resource layer needs from `usePluginOverrides`. + * All plugins expose these — `apiBaseURL`/`apiBasePath` directly, the router + * fields via the top-level `router` prop merge on `StackProvider`. + */ +export interface ResourceOverrides { + apiBaseURL: string; + apiBasePath: string; + headers?: HeadersInit; + navigate?: (path: string) => void | Promise; + refresh?: () => void | Promise; +} + +export interface ResourceContext { + client: ResourceClient; + headers?: HeadersInit; + navigate?: (path: string) => void | Promise; + refresh?: () => void | Promise; +} + +/** Resolves the plugin overrides and builds the better-call client. */ +export function useResourceContext(plugin: string): ResourceContext { + const { apiBaseURL, apiBasePath, headers, navigate, refresh } = + usePluginOverrides(plugin); + const client = createApiClient({ + baseURL: apiBaseURL, + basePath: apiBasePath, + }); + return { client, headers, navigate, refresh }; +} + +/** + * Splits an `invalidates` target (`"posts"` or `"posts.list"`) into a + * query-key prefix. + */ +function invalidateTargetToKey(target: string): readonly unknown[] { + const dotIndex = target.indexOf("."); + if (dotIndex === -1) return [target]; + return [target.slice(0, dotIndex), target.slice(dotIndex + 1)]; +} + +/** + * Shared mutation hook used by both the generated mutation hooks and + * `useForm`. When `def` is undefined (e.g. `useForm` on a resource without a + * declared `create` mutation), the mutation rejects with a descriptive error. + */ +export function useResourceMutationForDef( + context: ResourceContext, + resourceName: string, + mutationName: string, + resource: ResourceDef, + def: ResourceMutationDef | undefined, +): UseMutationResult { + const queryClient = useQueryClient(); + const { client, refresh } = context; + + return useMutation({ + mutationKey: [resourceName, mutationName], + mutationFn: (vars: unknown) => { + if (!def) { + throw new Error( + `Resource "${resourceName}" has no "${mutationName}" mutation declared`, + ); + } + return runResourceMutation(client, def, vars); + }, + onSuccess: async (result) => { + if (!def) return; + + // Seed a query cache entry (e.g. detail) from the mutation result + if (def.setData) { + const keyArgs = def.setData.args(result); + const targetName = def.setData.query ?? "detail"; + const targetDef = resource.queries[targetName]; + if (keyArgs && targetDef) { + queryClient.setQueryData( + buildQueryKey(resourceName, targetName, targetDef, keyArgs), + result, + ); + } + } + + // Invalidate declared key prefixes — awaited, in declaration order + for (const target of def.invalidates ?? []) { + await queryClient.invalidateQueries({ + queryKey: invalidateTargetToKey(target), + }); + } + + // Refresh server-side cache (e.g. Next.js router cache) + if (refresh) { + await refresh(); + } + }, + }); +} diff --git a/packages/stack/src/plugins/client/resource/queries.ts b/packages/stack/src/plugins/client/resource/queries.ts new file mode 100644 index 000000000..12008e6d4 --- /dev/null +++ b/packages/stack/src/plugins/client/resource/queries.ts @@ -0,0 +1,279 @@ +/** + * Server-safe resource declaration types and query-key/queryFn builder. + * + * A plugin declares its resources once (paths, query mappings, unwrapping, + * key discriminators) and gets: + * + * - `createResourceQueryKeys(client, resources, headers?)` — a query-key + * factory usable from SSR loaders and `query-keys.ts` (no React), with the + * same `_def` / `queryKey` shapes as `@lukemorales/query-key-factory`, so + * existing SSG `prefetchForRoute` / `query-key-defs.ts` keys keep matching. + * - `createResource(...)` (see `./hooks`) — generated React Query hooks. + */ + +import { isErrorResponse, toError } from "./errors"; + +/** + * Minimal better-call client shape the resource layer needs. + * Any `createApiClient()` result is assignable. + */ +export type ResourceClient = (path: any, options?: any) => Promise; + +/** + * Declaration for a single query on a resource. + * + * Query-key shape: `[resourceName, queryName, ...key(...args)]` where `key` + * defaults to the args themselves. Share discriminator functions with + * `api/query-key-defs.ts` to keep SSG prefetch keys in sync. + */ +export interface ResourceQueryDef< + TArgs extends readonly unknown[] = readonly any[], + TData = unknown, +> { + /** better-call endpoint path, e.g. `"/posts"` */ + path: string; + /** Maps hook args to the HTTP query object */ + query?: (...args: TArgs) => Record | undefined; + /** + * Maps hook args to the queryKey discriminator cells appended after + * `[resourceName, queryName]`. Defaults to the args themselves. + */ + key?: (...args: TArgs) => readonly unknown[]; + /** Unwraps/selects data from the raw response data */ + select?: (data: any, ...args: TArgs) => TData; + /** + * Offset-paginated infinite query. The queryFn receives `pageParam` and + * injects it into the HTTP query as `offsetParam`. `select` must return + * an array (one page of items). + */ + infinite?: boolean; + /** HTTP query param carrying the page offset (default `"offset"`) */ + offsetParam?: string; + /** + * Page size used to derive `getNextPageParam` for infinite queries + * (default 10). A function form derives it from the hook args. + */ + pageSize?: number | ((...args: TArgs) => number); + /** When true, skip fetching and resolve `null` (e.g. missing id) */ + skip?: (...args: TArgs) => boolean; +} + +/** + * Declaration for a single mutation on a resource. + */ +export interface ResourceMutationDef { + /** better-call endpoint path, e.g. `"@post/posts"` or `"@put/posts/:id"` */ + path: string; + method: "POST" | "PUT" | "PATCH" | "DELETE"; + /** + * Maps mutation variables to better-call call options. + * Defaults to `{ body: vars }`. + */ + input?: (vars: TVars) => { + body?: unknown; + params?: Record; + query?: Record; + }; + /** Unwraps the mutation result from the raw response data */ + select?: (data: any) => TResult; + /** + * Query-key prefixes invalidated (awaited, in order) after success. + * `"posts"` invalidates the whole resource, `"posts.list"` one query. + */ + invalidates?: readonly string[]; + /** + * Seed a query cache entry from the mutation result (e.g. the detail + * entry for a created/updated record). `args` returns the key args for + * the target query, or `null` to skip seeding. + */ + setData?: { + /** Target query name on the same resource (default `"detail"`) */ + query?: string; + args: (result: TResult) => readonly unknown[] | null; + }; +} + +/** Declaration for one resource: its queries and (optionally) mutations. */ +export interface ResourceDef { + queries: Record>; + mutations?: Record>; +} + +/** A plugin's full resource declaration, keyed by resource name. */ +export type ResourcesDeclaration = Record; + +/** Extracts the hook args tuple from a query declaration. */ +export type ResourceQueryArgs = TDef extends { + query: (...args: infer A) => any; +} + ? A + : TDef extends { key: (...args: infer A) => any } + ? A + : TDef extends { select: (data: any, ...args: infer A) => any } + ? A + : []; + +/** Extracts the (per-page, for infinite queries) data type from a query declaration. */ +export type ResourceQueryData = TDef extends { + select: (data: any, ...args: any[]) => infer D; +} + ? D + : unknown; + +/** Extracts the variables type from a mutation declaration. */ +export type ResourceMutationVars = TDef extends { + input: (vars: infer V) => any; +} + ? V + : unknown; + +/** Extracts the result type from a mutation declaration. */ +export type ResourceMutationResult = TDef extends { + select: (data: any) => infer R; +} + ? R + : unknown; + +/** + * A query-key factory entry: call with args to get `{ queryKey, queryFn }`, + * read `_def` for the `[resourceName, queryName]` prefix. + */ +export interface ResourceQueryEntry< + TArgs extends readonly unknown[] = readonly any[], + TData = unknown, +> { + ( + ...args: TArgs + ): { + queryKey: readonly unknown[]; + queryFn: (context?: { pageParam?: unknown }) => Promise; + }; + _def: readonly [string, string]; +} + +/** The query-key factory produced from a resources declaration. */ +export type ResourceQueryKeys = { + [R in keyof TResources]: { + [Q in keyof TResources[R]["queries"]]: ResourceQueryEntry< + ResourceQueryArgs, + ResourceQueryData + >; + } & { _def: readonly [R] }; +}; + +/** Resolves the effective page size for an infinite query declaration. */ +export function resolvePageSize( + def: ResourceQueryDef, + args: readonly unknown[], +): number { + if (typeof def.pageSize === "function") return def.pageSize(...args); + return def.pageSize ?? 10; +} + +/** Builds the full query key for a query declaration and args. */ +export function buildQueryKey( + resourceName: string, + queryName: string, + def: ResourceQueryDef, + args: readonly unknown[], +): readonly unknown[] { + const cells = def.key ? def.key(...args) : args; + return [resourceName, queryName, ...cells]; +} + +/** + * Executes the fetch → error-check → unwrap dance for a query declaration. + */ +export async function runResourceQuery( + client: ResourceClient, + def: ResourceQueryDef, + args: readonly unknown[], + pageParam?: unknown, + headers?: HeadersInit, +): Promise { + if (def.skip?.(...args)) return null; + + const baseQuery = def.query?.(...args); + const query = def.infinite + ? { ...baseQuery, [def.offsetParam ?? "offset"]: pageParam ?? 0 } + : baseQuery; + + const response = await client(def.path, { + method: "GET", + ...(query !== undefined ? { query } : {}), + ...(headers !== undefined ? { headers } : {}), + }); + + if (isErrorResponse(response)) { + throw toError(response.error); + } + + const data = (response as { data?: unknown }).data; + return def.select ? def.select(data, ...args) : data; +} + +/** + * Executes a mutation declaration: fetch → error-check → unwrap. + */ +export async function runResourceMutation( + client: ResourceClient, + def: ResourceMutationDef, + vars: unknown, +): Promise { + const { body, params, query } = def.input + ? def.input(vars) + : { body: vars, params: undefined, query: undefined }; + + const response = await client(def.path, { + method: def.method, + ...(body !== undefined ? { body } : {}), + ...(params !== undefined ? { params } : {}), + ...(query !== undefined ? { query } : {}), + }); + + if (isErrorResponse(response)) { + throw toError(response.error); + } + + const data = (response as { data?: unknown }).data; + return def.select ? def.select(data) : data; +} + +/** + * Builds a query-key factory from a resources declaration. + * + * Compatible with the shapes `@lukemorales/query-key-factory` produces: + * `store.posts.list(params)` → `{ queryKey: ["posts", "list", ...], queryFn }`, + * `store.posts.list._def` → `["posts", "list"]`, `store.posts._def` → `["posts"]`. + * + * Server-safe (no React) — usable from SSR loaders and `query-keys.ts`. + */ +export function createResourceQueryKeys< + const TResources extends ResourcesDeclaration, +>( + client: ResourceClient, + resources: TResources, + headers?: HeadersInit, +): ResourceQueryKeys { + const store: Record = {}; + + for (const [resourceName, resource] of Object.entries(resources)) { + const resourceStore: Record = { + _def: [resourceName] as const, + }; + + for (const [queryName, def] of Object.entries(resource.queries)) { + const entry = (...args: readonly unknown[]) => ({ + queryKey: buildQueryKey(resourceName, queryName, def, args), + queryFn: (context?: { pageParam?: unknown }) => + runResourceQuery(client, def, args, context?.pageParam, headers), + }); + entry._def = [resourceName, queryName] as const; + resourceStore[queryName] = entry; + } + + store[resourceName] = resourceStore; + } + + return store as ResourceQueryKeys; +} diff --git a/packages/stack/src/plugins/client/resource/use-debounce.ts b/packages/stack/src/plugins/client/resource/use-debounce.ts new file mode 100644 index 000000000..008a4549e --- /dev/null +++ b/packages/stack/src/plugins/client/resource/use-debounce.ts @@ -0,0 +1,18 @@ +"use client"; + +import { useEffect, useState } from "react"; + +/** Returns `value` debounced by `delay` milliseconds (default 500). */ +export function useDebounce(value: T, delay?: number): T { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect(() => { + const timer = setTimeout(() => setDebouncedValue(value), delay || 500); + + return () => { + clearTimeout(timer); + }; + }, [value, delay]); + + return debouncedValue; +} diff --git a/packages/stack/src/plugins/client/resource/use-form.ts b/packages/stack/src/plugins/client/resource/use-form.ts new file mode 100644 index 000000000..50011790f --- /dev/null +++ b/packages/stack/src/plugins/client/resource/use-form.ts @@ -0,0 +1,236 @@ +"use client"; + +import { useQuery } from "@tanstack/react-query"; +import { useMemo, useRef, useState } from "react"; +import { useNotify } from "../../../context"; +import { SHARED_QUERY_CONFIG, toError, type StackError } from "./errors"; +import { buildQueryKey, runResourceQuery, type ResourceDef } from "./queries"; +import { useResourceContext, useResourceMutationForDef } from "./internal"; + +/** Configuration for the per-resource `useForm` hook. */ +export interface ResourceFormConfig< + TValues, + TRecord = unknown, + TResult = unknown, +> { + action: "create" | "edit"; + /** Detail-query arg identifying the record to edit (e.g. a slug or id) */ + id?: unknown; + /** Query used to fetch the record for edit (default `"detail"`) */ + detailQuery?: string; + /** Mutation used on create submit (default `"create"`) */ + createMutation?: string; + /** Mutation used on edit submit (default `"update"`) */ + updateMutation?: string; + /** + * Externally supplied record — skips the internal detail fetch. Useful + * when the record comes from a suspense hook higher in the tree. + */ + record?: TRecord | null; + /** Default form values, or a function deriving them from the record */ + defaults?: TValues | ((record: TRecord | null) => TValues); + /** Maps form values to create-mutation variables (default: identity) */ + toCreateVars?: (values: TValues) => unknown; + /** Maps form values to update-mutation variables (default: identity) */ + toUpdateVars?: (values: TValues, record: TRecord | null) => unknown; + /** Success notification, sent through the `notify` provider */ + successMessage?: + | string + | ((result: TResult, action: "create" | "edit") => string); + /** + * Error notification for non-field errors. Field-level validation errors + * land on `fieldErrors` instead of producing a notification. + */ + errorMessage?: string | ((error: StackError) => string); + /** + * Path to navigate to after success (via the router adapter's + * `navigate`). A function form derives it from the result; return a + * falsy value to skip navigation. + */ + redirect?: + | string + | (( + result: TResult, + action: "create" | "edit", + ) => string | false | null | undefined); + /** Called after a successful submit (before redirect) */ + onSuccess?: (result: TResult) => void | Promise; +} + +/** Result of the per-resource `useForm` hook. */ +export interface ResourceFormResult< + TValues, + TRecord = unknown, + TResult = unknown, +> { + action: "create" | "edit"; + /** The record being edited (null while loading or when creating) */ + record: TRecord | null; + isLoadingRecord: boolean; + recordError: Error | null; + /** Resolved default values (stable while `record` is unchanged) */ + defaultValues: TValues | undefined; + /** Runs the right mutation; resolves the result, or undefined on error */ + submit: (values: TValues) => Promise; + isSubmitting: boolean; + /** Last submit error (normalized), or null */ + error: StackError | null; + /** Field name → validation message(s) from the last submit error */ + fieldErrors: Record; + clearErrors: () => void; +} + +/** + * Builds the per-resource `useForm` hook: bundles the create/edit lifecycle — + * fetch record for edit, defaults, submit the right mutation, invalidate, + * notify, redirect, and per-field error state from `StackError.errors`. + */ +export function createUseForm( + plugin: string, + resourceName: string, + resource: ResourceDef, +) { + return function useForm( + config: ResourceFormConfig, + ): ResourceFormResult { + const context = useResourceContext(plugin); + const notify = useNotify(); + const { action } = config; + + // --- record fetch (edit) --------------------------------------------- + const detailName = config.detailQuery ?? "detail"; + const detailDef = resource.queries[detailName]; + const hasExternalRecord = config.record !== undefined; + const detailArgs = config.id !== undefined ? [config.id] : []; + const detailEnabled = + action === "edit" && + !hasExternalRecord && + config.id !== undefined && + !!detailDef; + + const recordQuery = useQuery({ + queryKey: detailDef + ? buildQueryKey(resourceName, detailName, detailDef, detailArgs) + : [resourceName, detailName], + queryFn: () => { + if (!detailDef) { + // Unreachable: the query is disabled when detailDef is missing + throw new Error( + `Resource "${resourceName}" has no "${detailName}" query declared`, + ); + } + return runResourceQuery( + context.client, + detailDef, + detailArgs, + undefined, + context.headers, + ); + }, + ...SHARED_QUERY_CONFIG, + enabled: detailEnabled, + }); + + const record = hasExternalRecord + ? (config.record ?? null) + : ((recordQuery.data as TRecord | undefined) ?? null); + + // --- defaults ---------------------------------------------------------- + const defaultsRef = useRef(config.defaults); + defaultsRef.current = config.defaults; + const defaultValues = useMemo(() => { + const defaults = defaultsRef.current; + if (typeof defaults === "function") { + return (defaults as (record: TRecord | null) => TValues)(record); + } + return defaults; + // The defaults function itself is intentionally not a dependency — + // it is typically an inline closure; only record changes matter. + }, [record]); + + // --- mutations --------------------------------------------------------- + const createName = config.createMutation ?? "create"; + const updateName = config.updateMutation ?? "update"; + const createMutation = useResourceMutationForDef( + context, + resourceName, + createName, + resource, + resource.mutations?.[createName], + ); + const updateMutation = useResourceMutationForDef( + context, + resourceName, + updateName, + resource, + resource.mutations?.[updateName], + ); + + // --- submit ------------------------------------------------------------ + const [error, setError] = useState(null); + + const submit = async (values: TValues): Promise => { + setError(null); + try { + const isEdit = action === "edit"; + const vars = isEdit + ? config.toUpdateVars + ? config.toUpdateVars(values, record) + : values + : config.toCreateVars + ? config.toCreateVars(values) + : values; + const mutation = isEdit ? updateMutation : createMutation; + const result = (await mutation.mutateAsync(vars)) as TResult; + + if (config.successMessage) { + notify.success( + typeof config.successMessage === "function" + ? config.successMessage(result, action) + : config.successMessage, + ); + } + if (config.onSuccess) { + await config.onSuccess(result); + } + if (config.redirect) { + const path = + typeof config.redirect === "function" + ? config.redirect(result, action) + : config.redirect; + if (path) { + await context.navigate?.(path); + } + } + return result; + } catch (e) { + const stackError = toError(e); + setError(stackError); + // Field-level validation errors land on fieldErrors, not a toast + if (!stackError.errors && config.errorMessage) { + notify.error( + typeof config.errorMessage === "function" + ? config.errorMessage(stackError) + : config.errorMessage, + ); + } + return undefined; + } + }; + + const fieldErrors = useMemo(() => error?.errors ?? {}, [error]); + + return { + action, + record, + isLoadingRecord: detailEnabled ? recordQuery.isLoading : false, + recordError: detailEnabled ? recordQuery.error : null, + defaultValues, + submit, + isSubmitting: createMutation.isPending || updateMutation.isPending, + error, + fieldErrors, + clearErrors: () => setError(null), + }; + }; +} diff --git a/packages/stack/src/plugins/client/resource/use-select.ts b/packages/stack/src/plugins/client/resource/use-select.ts new file mode 100644 index 000000000..a2173bf8c --- /dev/null +++ b/packages/stack/src/plugins/client/resource/use-select.ts @@ -0,0 +1,196 @@ +"use client"; + +import { useQueries, useQuery } from "@tanstack/react-query"; +import { useState } from "react"; +import { SHARED_QUERY_CONFIG } from "./errors"; +import { buildQueryKey, runResourceQuery, type ResourceDef } from "./queries"; +import { useResourceContext } from "./internal"; +import { useDebounce } from "./use-debounce"; + +/** An option produced by the per-resource `useSelect` hook. */ +export interface ResourceSelectOption { + value: string; + label: string; + /** The underlying record; undefined for values that could not be resolved */ + item?: TItem; +} + +/** Configuration for the per-resource `useSelect` hook. */ +export interface ResourceSelectConfig { + /** + * Query used to fetch options (default `"list"`). Must be a non-infinite + * query whose data is an array of records. + */ + query?: string; + /** Maps the (debounced) search text to the option-query args */ + searchArgs: (search: string) => readonly unknown[]; + getOptionValue: (item: TItem) => string; + getOptionLabel: (item: TItem) => string; + /** Currently selected value(s) — preloaded when missing from options */ + value?: string | string[]; + /** + * Query used to preload selected values missing from the options + * (default query `"detail"`). Preloading is skipped when omitted. + */ + preload?: { + query?: string; + args: (value: string) => readonly unknown[]; + }; + /** Debounce for the search text in milliseconds (default 300) */ + debounceMs?: number; + enabled?: boolean; +} + +/** Result of the per-resource `useSelect` hook. */ +export interface ResourceSelectResult { + /** Search results plus preloaded selected records */ + options: ResourceSelectOption[]; + /** Options for the current `value(s)`, resolved where possible */ + selectedOptions: ResourceSelectOption[]; + search: string; + setSearch: (search: string) => void; + /** Initial load of options or preloaded values in flight */ + isLoading: boolean; + /** Search debounce pending or a search fetch in flight */ + isSearching: boolean; + error: Error | null; +} + +/** + * Builds the per-resource `useSelect` hook: debounced server-side search, + * current-value preloading, and loading states for relation pickers. + */ +export function createUseSelect( + plugin: string, + resourceName: string, + resource: ResourceDef, +) { + return function useSelect( + config: ResourceSelectConfig, + ): ResourceSelectResult { + const context = useResourceContext(plugin); + const enabled = config.enabled ?? true; + + // --- search ------------------------------------------------------------ + const [search, setSearch] = useState(""); + const debouncedSearch = useDebounce(search, config.debounceMs ?? 300); + + const listName = config.query ?? "list"; + const listDef = resource.queries[listName]; + if (!listDef) { + throw new Error( + `Resource "${resourceName}" has no "${listName}" query declared`, + ); + } + if (listDef.infinite) { + throw new Error( + `useSelect requires a non-infinite query, but "${resourceName}.${listName}" is declared infinite`, + ); + } + + const listArgs = config.searchArgs(debouncedSearch); + const listQuery = useQuery({ + queryKey: buildQueryKey(resourceName, listName, listDef, listArgs), + queryFn: () => + runResourceQuery( + context.client, + listDef, + listArgs, + undefined, + context.headers, + ), + ...SHARED_QUERY_CONFIG, + enabled, + }); + + const items = (listQuery.data as TItem[] | null | undefined) ?? []; + + // --- current-value preloading ------------------------------------------- + const values = + config.value === undefined + ? [] + : Array.isArray(config.value) + ? config.value + : [config.value]; + + const { getOptionValue, getOptionLabel } = config; + const fetchedValues = new Set(items.map((item) => getOptionValue(item))); + const preloadName = config.preload?.query ?? "detail"; + const preloadDef = config.preload + ? resource.queries[preloadName] + : undefined; + // Wait for the initial options load before preloading, so values that + // are part of the regular options don't trigger a redundant fetch. + const missingValues = + preloadDef && enabled && !listQuery.isLoading + ? values.filter((value) => !fetchedValues.has(value)) + : []; + + const preloadQueries = useQueries({ + queries: missingValues.map((value) => { + const args = ( + config.preload as NonNullable + ).args(value); + return { + queryKey: buildQueryKey( + resourceName, + preloadName, + preloadDef as NonNullable, + args, + ), + queryFn: () => + runResourceQuery( + context.client, + preloadDef as NonNullable, + args, + undefined, + context.headers, + ), + ...SHARED_QUERY_CONFIG, + }; + }), + }); + + const preloadedItems = preloadQueries + .map((query) => query.data as TItem | null | undefined) + .filter((item): item is TItem => item !== null && item !== undefined); + + // --- options ------------------------------------------------------------- + const toOption = (item: TItem): ResourceSelectOption => ({ + value: getOptionValue(item), + label: getOptionLabel(item), + item, + }); + + const options: ResourceSelectOption[] = items.map(toOption); + const seen = new Set(options.map((option) => option.value)); + for (const item of preloadedItems) { + const option = toOption(item); + if (!seen.has(option.value)) { + seen.add(option.value); + options.push(option); + } + } + + const selectedOptions = values.map( + (value) => + options.find((option) => option.value === value) ?? { + value, + label: value, + }, + ); + + const isDebouncing = search !== debouncedSearch; + + return { + options, + selectedOptions, + search, + setSearch, + isLoading: + listQuery.isLoading || preloadQueries.some((query) => query.isLoading), + isSearching: enabled && (isDebouncing || listQuery.isFetching), + error: listQuery.error, + }; + }; +} From 55411533ef091884192b2d26124829d1bc3310e1 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 22:00:20 +0000 Subject: [PATCH 022/380] fix: expose usePostForm publicly so ejected registry components compile Co-authored-by: Cursor --- packages/stack/registry/btst-blog.json | 4 ++-- .../client/components/forms/post-forms.tsx | 17 +++++++---------- .../plugins/blog/client/hooks/blog-hooks.tsx | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/packages/stack/registry/btst-blog.json b/packages/stack/registry/btst-blog.json index 9da404789..215287474 100644 --- a/packages/stack/registry/btst-blog.json +++ b/packages/stack/registry/btst-blog.json @@ -82,7 +82,7 @@ { "path": "btst/blog/client/components/forms/post-forms.tsx", "type": "registry:component", - "content": "\"use client\";\nimport {\n\tcreatePostSchema as PostCreateSchema,\n\tupdatePostSchema as PostUpdateSchema,\n} from \"../../../schemas\";\n\nimport { Button } from \"@/components/ui/button\";\n\nimport {\n\tForm,\n\tFormControl,\n\tFormDescription,\n\tFormField,\n\tFormItem,\n\tFormLabel,\n\tFormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\n\nimport { Switch } from \"@/components/ui/switch\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport {\n\tuseCreatePost,\n\tuseSuspensePost,\n\tuseUpdatePost,\n\tuseDeletePost,\n} from \"@btst/stack/plugins/blog/client/hooks\";\nimport { slugify } from \"../../../utils\";\nimport {\n\tAlertDialog,\n\tAlertDialogAction,\n\tAlertDialogCancel,\n\tAlertDialogContent,\n\tAlertDialogDescription,\n\tAlertDialogFooter,\n\tAlertDialogHeader,\n\tAlertDialogTitle,\n\tAlertDialogTrigger,\n} from \"@/components/ui/alert-dialog\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { Loader2 } from \"lucide-react\";\nimport { lazy, memo, Suspense, useEffect, useMemo, useState } from \"react\";\nimport {\n\ttype FieldPath,\n\ttype SubmitHandler,\n\ttype UseFormReturn,\n\tuseForm,\n} from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\nimport { FeaturedImageField } from \"./image-field\";\n\nconst MarkdownEditor = lazy(() =>\n\timport(\"./markdown-editor-with-overrides\").then((module) => ({\n\t\tdefault: module.MarkdownEditorWithOverrides,\n\t})),\n);\nimport { BLOG_LOCALIZATION } from \"../../localization\";\nimport { usePluginOverrides } from \"@btst/stack/context\";\nimport type { BlogPluginOverrides } from \"../../overrides\";\nimport { EmptyList } from \"../shared/empty-list\";\nimport { TagsMultiSelect } from \"./tags-multiselect\";\n\ntype CommonPostFormValues = {\n\ttitle: string;\n\tcontent: string;\n\texcerpt?: string;\n\tslug?: string;\n\timage?: string;\n\tpublished?: boolean;\n\ttags?: Array<{ name: string } | { id: string; name: string; slug: string }>;\n};\n\nfunction PostFormBody({\n\tform,\n\tonSubmit,\n\tsubmitLabel,\n\tonCancel,\n\tdisabled,\n\terrorMessage,\n\tsetFeaturedImageUploading,\n\tinitialSlugTouched = false,\n}: {\n\tform: UseFormReturn;\n\tonSubmit: SubmitHandler;\n\tsubmitLabel: string;\n\tonCancel: () => void;\n\tdisabled: boolean;\n\terrorMessage?: string;\n\tsetFeaturedImageUploading: (uploading: boolean) => void;\n\tinitialSlugTouched?: boolean;\n}) {\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\tconst [slugTouched, setSlugTouched] = useState(initialSlugTouched);\n\tconst nameTitle = \"title\" as FieldPath;\n\tconst nameSlug = \"slug\" as FieldPath;\n\tconst nameExcerpt = \"excerpt\" as FieldPath;\n\tconst nameImage = \"image\" as FieldPath;\n\tconst nameTags = \"tags\" as FieldPath;\n\tconst nameContent = \"content\" as FieldPath;\n\tconst namePublished = \"published\" as FieldPath;\n\treturn (\n\t\t
\n\t\t\t\n\t\t\t\t{errorMessage && (\n\t\t\t\t\t
\n\t\t\t\t\t\t{errorMessage}\n\t\t\t\t\t
\n\t\t\t\t)}\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_TITLE_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\tconst newTitle = e.target.value;\n\t\t\t\t\t\t\t\t\t\tfield.onChange(e);\n\t\t\t\t\t\t\t\t\t\t// Auto-slugify title if slug is not yet set\n\t\t\t\t\t\t\t\t\t\tif (!slugTouched) {\n\t\t\t\t\t\t\t\t\t\t\t// @ts-expect-error - slugify returns string which is compatible with slug field type\n\t\t\t\t\t\t\t\t\t\t\tform.setValue(nameSlug, slugify(newTitle));\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t {\n\t\t\t\t\t\tconst currentTitle = form.getValues(nameTitle);\n\t\t\t\t\t\tconst autoGeneratedSlug = slugify(String(currentTitle ?? \"\"));\n\t\t\t\t\t\tconst currentSlug = String(field.value ?? \"\");\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_SLUG_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\tconst newSlug = e.target.value;\n\t\t\t\t\t\t\t\t\t\t\tfield.onChange(e);\n\t\t\t\t\t\t\t\t\t\t\t// Only mark as touched if the user manually edited to something different from auto-generated\n\t\t\t\t\t\t\t\t\t\t\t// This allows auto-generation to continue if the slug matches what would be generated\n\t\t\t\t\t\t\t\t\t\t\tif (newSlug !== autoGeneratedSlug) {\n\t\t\t\t\t\t\t\t\t\t\t\tsetSlugTouched(true);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_EXCERPT_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{localization.BLOG_FORMS_TAGS_LABEL}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_CONTENT_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\tfield.onChange(content);\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_PUBLISHED_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_PUBLISHED_DESCRIPTION}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{localization.BLOG_FORMS_CANCEL_BUTTON}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\n\t\t\n\t);\n}\n\nconst CustomPostCreateSchema = PostCreateSchema.omit({\n\tcreatedAt: true,\n\tupdatedAt: true,\n\tpublishedAt: true,\n});\n\nconst CustomPostUpdateSchema = PostUpdateSchema.omit({\n\tid: true,\n\tcreatedAt: true,\n\tupdatedAt: true,\n\tpublishedAt: true,\n});\n\ntype AddPostFormProps = {\n\tonClose: () => void;\n\tonSuccess: (post: { published: boolean }) => void;\n\t/** Called once with the form instance so parent components can access form state */\n\tonFormReady?: (\n\t\tform: UseFormReturn>,\n\t) => void;\n};\n\nconst addPostFormPropsAreEqual = (\n\tprevProps: AddPostFormProps,\n\tnextProps: AddPostFormProps,\n): boolean => {\n\tif (prevProps.onClose !== nextProps.onClose) return false;\n\tif (prevProps.onSuccess !== nextProps.onSuccess) return false;\n\tif (prevProps.onFormReady !== nextProps.onFormReady) return false;\n\treturn true;\n};\n\nconst AddPostFormComponent = ({\n\tonClose,\n\tonSuccess,\n\tonFormReady,\n}: AddPostFormProps) => {\n\tconst [featuredImageUploading, setFeaturedImageUploading] = useState(false);\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\n\t// const { uploadImage } = useBlogContext()\n\n\tconst schema = CustomPostCreateSchema;\n\n\tconst {\n\t\tmutateAsync: createPost,\n\t\tisPending: isCreatingPost,\n\t\terror: createPostError,\n\t} = useCreatePost();\n\n\ttype AddPostFormValues = z.input;\n\tconst onSubmit = async (data: AddPostFormValues) => {\n\t\t// Auto-generate slug from title if not provided\n\t\tconst slug = data.slug || slugify(data.title);\n\n\t\t// Wait for mutation to complete, including refresh\n\t\tconst createdPost = await createPost({\n\t\t\ttitle: data.title,\n\t\t\tcontent: data.content,\n\t\t\texcerpt: data.excerpt ?? \"\",\n\t\t\tslug,\n\t\t\tpublished: data.published ?? false,\n\t\t\tpublishedAt: data.published ? new Date() : undefined,\n\t\t\timage: data.image,\n\t\t\ttags: data.tags || [],\n\t\t});\n\n\t\ttoast.success(localization.BLOG_FORMS_TOAST_CREATE_SUCCESS);\n\n\t\t// Navigate only after mutation completes\n\t\tonSuccess({ published: createdPost?.published ?? false });\n\t};\n\n\t// For compatibility with resolver types that require certain required fields,\n\t// cast the generics to the exact inferred input type to avoid mismatch on optional slug\n\tconst form = useForm>({\n\t\tresolver: zodResolver(schema),\n\t\tdefaultValues: {\n\t\t\ttitle: \"\",\n\t\t\tcontent: \"\",\n\t\t\texcerpt: \"\",\n\t\t\tslug: undefined,\n\t\t\tpublished: false,\n\t\t\timage: \"\",\n\t\t\ttags: [],\n\t\t},\n\t});\n\n\t// Expose form instance to parent for AI context integration\n\tuseEffect(() => {\n\t\tonFormReady?.(form);\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, []);\n\n\treturn (\n\t\t\n\t);\n};\n\nexport const AddPostForm = memo(AddPostFormComponent, addPostFormPropsAreEqual);\n\ntype EditPostFormProps = {\n\tpostSlug: string;\n\tonClose: () => void;\n\tonSuccess: (post: { slug: string; published: boolean }) => void;\n\tonDelete?: () => void;\n\t/** Called once with the form instance so parent components can access form state */\n\tonFormReady?: (\n\t\tform: UseFormReturn>,\n\t) => void;\n};\n\nconst editPostFormPropsAreEqual = (\n\tprevProps: EditPostFormProps,\n\tnextProps: EditPostFormProps,\n): boolean => {\n\tif (prevProps.postSlug !== nextProps.postSlug) return false;\n\tif (prevProps.onClose !== nextProps.onClose) return false;\n\tif (prevProps.onSuccess !== nextProps.onSuccess) return false;\n\tif (prevProps.onDelete !== nextProps.onDelete) return false;\n\tif (prevProps.onFormReady !== nextProps.onFormReady) return false;\n\treturn true;\n};\n\nconst EditPostFormComponent = ({\n\tpostSlug,\n\tonClose,\n\tonSuccess,\n\tonDelete,\n\tonFormReady,\n}: EditPostFormProps) => {\n\tconst [featuredImageUploading, setFeaturedImageUploading] = useState(false);\n\tconst [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\t// const { uploadImage } = useBlogContext()\n\n\tconst { post } = useSuspensePost(postSlug);\n\n\tconst initialData = useMemo(() => {\n\t\tif (!post) return {};\n\t\treturn {\n\t\t\ttitle: post.title,\n\t\t\tcontent: post.content,\n\t\t\texcerpt: post.excerpt,\n\t\t\tslug: post.slug,\n\t\t\tpublished: post.published,\n\t\t\timage: post.image || \"\",\n\t\t\ttags: post.tags.map((tag) => ({\n\t\t\t\tid: tag.id,\n\t\t\t\tname: tag.name,\n\t\t\t\tslug: tag.slug,\n\t\t\t})),\n\t\t};\n\t}, [post]);\n\n\tconst schema = CustomPostUpdateSchema;\n\n\tconst {\n\t\tmutateAsync: updatePost,\n\t\tisPending: isUpdatingPost,\n\t\terror: updatePostError,\n\t} = useUpdatePost();\n\n\tconst { mutateAsync: deletePost, isPending: isDeletingPost } =\n\t\tuseDeletePost();\n\n\ttype EditPostFormValues = z.input;\n\tconst onSubmit = async (data: EditPostFormValues) => {\n\t\t// Wait for mutation to complete, including refresh\n\t\tconst updatedPost = await updatePost({\n\t\t\tid: post!.id,\n\t\t\tdata: {\n\t\t\t\tid: post!.id,\n\t\t\t\ttitle: data.title,\n\t\t\t\tcontent: data.content,\n\t\t\t\texcerpt: data.excerpt ?? \"\",\n\t\t\t\tslug: data.slug,\n\t\t\t\tpublished: data.published ?? false,\n\t\t\t\tpublishedAt:\n\t\t\t\t\tdata.published && !post?.published\n\t\t\t\t\t\t? new Date()\n\t\t\t\t\t\t: post?.publishedAt\n\t\t\t\t\t\t\t? new Date(post.publishedAt)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\timage: data.image,\n\t\t\t\ttags: data.tags || [],\n\t\t\t},\n\t\t});\n\n\t\ttoast.success(localization.BLOG_FORMS_TOAST_UPDATE_SUCCESS);\n\n\t\t// Navigate only after mutation completes\n\t\tonSuccess({\n\t\t\tslug: updatedPost?.slug ?? \"\",\n\t\t\tpublished: updatedPost?.published ?? false,\n\t\t});\n\t};\n\n\tconst handleDelete = async () => {\n\t\tif (!post?.id) return;\n\n\t\tawait deletePost({ id: post.id });\n\t\ttoast.success(localization.BLOG_FORMS_TOAST_DELETE_SUCCESS);\n\t\tsetDeleteDialogOpen(false);\n\n\t\t// Call onDelete callback if provided, otherwise use onClose\n\t\tif (onDelete) {\n\t\t\tonDelete();\n\t\t} else {\n\t\t\tonClose();\n\t\t}\n\t};\n\n\tconst form = useForm>({\n\t\tresolver: zodResolver(schema),\n\t\tdefaultValues: {\n\t\t\ttitle: \"\",\n\t\t\tcontent: \"\",\n\t\t\texcerpt: \"\",\n\t\t\tslug: \"\",\n\t\t\tpublished: false,\n\t\t\timage: \"\",\n\t\t\ttags: [],\n\t\t},\n\t\tvalues: initialData as z.input,\n\t});\n\n\t// Expose form instance to parent for AI context integration\n\tuseEffect(() => {\n\t\tonFormReady?.(form);\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, []);\n\n\tif (!post) {\n\t\treturn ;\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_BUTTON}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_TITLE}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_DESCRIPTION}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_CANCEL}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\tvoid handleDelete();\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tdisabled={isDeletingPost}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isDeletingPost\n\t\t\t\t\t\t\t\t\t? localization.BLOG_FORMS_DELETE_PENDING\n\t\t\t\t\t\t\t\t\t: localization.BLOG_FORMS_DELETE_DIALOG_CONFIRM}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\n\t);\n};\n\nexport const EditPostForm = memo(\n\tEditPostFormComponent,\n\teditPostFormPropsAreEqual,\n);\n", + "content": "\"use client\";\nimport {\n\tcreatePostSchema as PostCreateSchema,\n\tupdatePostSchema as PostUpdateSchema,\n} from \"../../../schemas\";\n\nimport { Button } from \"@/components/ui/button\";\n\nimport {\n\tForm,\n\tFormControl,\n\tFormDescription,\n\tFormField,\n\tFormItem,\n\tFormLabel,\n\tFormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\n\nimport { Switch } from \"@/components/ui/switch\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport {\n\tuseSuspensePost,\n\tuseDeletePost,\n\tusePostForm,\n} from \"@btst/stack/plugins/blog/client/hooks\";\nimport { slugify } from \"../../../utils\";\nimport type { SerializedPost } from \"../../../types\";\nimport {\n\tAlertDialog,\n\tAlertDialogAction,\n\tAlertDialogCancel,\n\tAlertDialogContent,\n\tAlertDialogDescription,\n\tAlertDialogFooter,\n\tAlertDialogHeader,\n\tAlertDialogTitle,\n\tAlertDialogTrigger,\n} from \"@/components/ui/alert-dialog\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { Loader2 } from \"lucide-react\";\nimport { lazy, memo, Suspense, useEffect, useState } from \"react\";\nimport {\n\ttype FieldPath,\n\ttype FieldValues,\n\ttype SubmitHandler,\n\ttype UseFormReturn,\n\tuseForm,\n} from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { FeaturedImageField } from \"./image-field\";\n\nconst MarkdownEditor = lazy(() =>\n\timport(\"./markdown-editor-with-overrides\").then((module) => ({\n\t\tdefault: module.MarkdownEditorWithOverrides,\n\t})),\n);\nimport { BLOG_LOCALIZATION } from \"../../localization\";\nimport { useNotify, usePluginOverrides } from \"@btst/stack/context\";\nimport type { BlogPluginOverrides } from \"../../overrides\";\nimport { EmptyList } from \"../shared/empty-list\";\nimport { TagsMultiSelect } from \"./tags-multiselect\";\n\n/**\n * Applies server-side field validation errors (from `StackError.errors`)\n * onto react-hook-form field state.\n */\nfunction useServerFieldErrors(\n\tform: UseFormReturn,\n\tfieldErrors: Record,\n) {\n\tuseEffect(() => {\n\t\tfor (const [field, message] of Object.entries(fieldErrors)) {\n\t\t\tform.setError(field as FieldPath, {\n\t\t\t\ttype: \"server\",\n\t\t\t\tmessage: Array.isArray(message) ? message.join(\", \") : message,\n\t\t\t});\n\t\t}\n\t}, [fieldErrors, form]);\n}\n\ntype CommonPostFormValues = {\n\ttitle: string;\n\tcontent: string;\n\texcerpt?: string;\n\tslug?: string;\n\timage?: string;\n\tpublished?: boolean;\n\ttags?: Array<{ name: string } | { id: string; name: string; slug: string }>;\n};\n\nfunction PostFormBody({\n\tform,\n\tonSubmit,\n\tsubmitLabel,\n\tonCancel,\n\tdisabled,\n\terrorMessage,\n\tsetFeaturedImageUploading,\n\tinitialSlugTouched = false,\n}: {\n\tform: UseFormReturn;\n\tonSubmit: SubmitHandler;\n\tsubmitLabel: string;\n\tonCancel: () => void;\n\tdisabled: boolean;\n\terrorMessage?: string;\n\tsetFeaturedImageUploading: (uploading: boolean) => void;\n\tinitialSlugTouched?: boolean;\n}) {\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\tconst [slugTouched, setSlugTouched] = useState(initialSlugTouched);\n\tconst nameTitle = \"title\" as FieldPath;\n\tconst nameSlug = \"slug\" as FieldPath;\n\tconst nameExcerpt = \"excerpt\" as FieldPath;\n\tconst nameImage = \"image\" as FieldPath;\n\tconst nameTags = \"tags\" as FieldPath;\n\tconst nameContent = \"content\" as FieldPath;\n\tconst namePublished = \"published\" as FieldPath;\n\treturn (\n\t\t
\n\t\t\t\n\t\t\t\t{errorMessage && (\n\t\t\t\t\t
\n\t\t\t\t\t\t{errorMessage}\n\t\t\t\t\t
\n\t\t\t\t)}\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_TITLE_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\tconst newTitle = e.target.value;\n\t\t\t\t\t\t\t\t\t\tfield.onChange(e);\n\t\t\t\t\t\t\t\t\t\t// Auto-slugify title if slug is not yet set\n\t\t\t\t\t\t\t\t\t\tif (!slugTouched) {\n\t\t\t\t\t\t\t\t\t\t\t// @ts-expect-error - slugify returns string which is compatible with slug field type\n\t\t\t\t\t\t\t\t\t\t\tform.setValue(nameSlug, slugify(newTitle));\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t {\n\t\t\t\t\t\tconst currentTitle = form.getValues(nameTitle);\n\t\t\t\t\t\tconst autoGeneratedSlug = slugify(String(currentTitle ?? \"\"));\n\t\t\t\t\t\tconst currentSlug = String(field.value ?? \"\");\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_SLUG_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\tconst newSlug = e.target.value;\n\t\t\t\t\t\t\t\t\t\t\tfield.onChange(e);\n\t\t\t\t\t\t\t\t\t\t\t// Only mark as touched if the user manually edited to something different from auto-generated\n\t\t\t\t\t\t\t\t\t\t\t// This allows auto-generation to continue if the slug matches what would be generated\n\t\t\t\t\t\t\t\t\t\t\tif (newSlug !== autoGeneratedSlug) {\n\t\t\t\t\t\t\t\t\t\t\t\tsetSlugTouched(true);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_EXCERPT_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{localization.BLOG_FORMS_TAGS_LABEL}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_CONTENT_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\tfield.onChange(content);\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_PUBLISHED_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_PUBLISHED_DESCRIPTION}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{localization.BLOG_FORMS_CANCEL_BUTTON}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\n\t\t\n\t);\n}\n\nconst CustomPostCreateSchema = PostCreateSchema.omit({\n\tcreatedAt: true,\n\tupdatedAt: true,\n\tpublishedAt: true,\n});\n\nconst CustomPostUpdateSchema = PostUpdateSchema.omit({\n\tid: true,\n\tcreatedAt: true,\n\tupdatedAt: true,\n\tpublishedAt: true,\n});\n\ntype AddPostFormProps = {\n\tonClose: () => void;\n\tonSuccess: (post: { published: boolean }) => void;\n\t/** Called once with the form instance so parent components can access form state */\n\tonFormReady?: (\n\t\tform: UseFormReturn>,\n\t) => void;\n};\n\nconst addPostFormPropsAreEqual = (\n\tprevProps: AddPostFormProps,\n\tnextProps: AddPostFormProps,\n): boolean => {\n\tif (prevProps.onClose !== nextProps.onClose) return false;\n\tif (prevProps.onSuccess !== nextProps.onSuccess) return false;\n\tif (prevProps.onFormReady !== nextProps.onFormReady) return false;\n\treturn true;\n};\n\nconst AddPostFormComponent = ({\n\tonClose,\n\tonSuccess,\n\tonFormReady,\n}: AddPostFormProps) => {\n\tconst [featuredImageUploading, setFeaturedImageUploading] = useState(false);\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\n\t// const { uploadImage } = useBlogContext()\n\n\tconst schema = CustomPostCreateSchema;\n\n\ttype AddPostFormValues = z.input;\n\n\tconst resourceForm = usePostForm({\n\t\taction: \"create\",\n\t\tsuccessMessage: localization.BLOG_FORMS_TOAST_CREATE_SUCCESS,\n\t\ttoCreateVars: (data) => ({\n\t\t\ttitle: data.title,\n\t\t\tcontent: data.content,\n\t\t\texcerpt: data.excerpt ?? \"\",\n\t\t\t// Auto-generate slug from title if not provided\n\t\t\tslug: data.slug || slugify(data.title),\n\t\t\tpublished: data.published ?? false,\n\t\t\tpublishedAt: data.published ? new Date() : undefined,\n\t\t\timage: data.image,\n\t\t\ttags: data.tags || [],\n\t\t}),\n\t\tonSuccess: (createdPost) => {\n\t\t\t// Navigate only after mutation (including invalidation) completes\n\t\t\tonSuccess({ published: createdPost?.published ?? false });\n\t\t},\n\t});\n\n\tconst onSubmit = async (data: AddPostFormValues) => {\n\t\tawait resourceForm.submit(data);\n\t};\n\n\t// For compatibility with resolver types that require certain required fields,\n\t// cast the generics to the exact inferred input type to avoid mismatch on optional slug\n\tconst form = useForm>({\n\t\tresolver: zodResolver(schema),\n\t\tdefaultValues: {\n\t\t\ttitle: \"\",\n\t\t\tcontent: \"\",\n\t\t\texcerpt: \"\",\n\t\t\tslug: undefined,\n\t\t\tpublished: false,\n\t\t\timage: \"\",\n\t\t\ttags: [],\n\t\t},\n\t});\n\n\t// Server-side Zod validation failures land on the matching form fields\n\tuseServerFieldErrors(form, resourceForm.fieldErrors);\n\tconst hasFieldErrors = Object.keys(resourceForm.fieldErrors).length > 0;\n\n\t// Expose form instance to parent for AI context integration\n\tuseEffect(() => {\n\t\tonFormReady?.(form);\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, []);\n\n\treturn (\n\t\t\n\t);\n};\n\nexport const AddPostForm = memo(AddPostFormComponent, addPostFormPropsAreEqual);\n\ntype EditPostFormProps = {\n\tpostSlug: string;\n\tonClose: () => void;\n\tonSuccess: (post: { slug: string; published: boolean }) => void;\n\tonDelete?: () => void;\n\t/** Called once with the form instance so parent components can access form state */\n\tonFormReady?: (\n\t\tform: UseFormReturn>,\n\t) => void;\n};\n\nconst editPostFormPropsAreEqual = (\n\tprevProps: EditPostFormProps,\n\tnextProps: EditPostFormProps,\n): boolean => {\n\tif (prevProps.postSlug !== nextProps.postSlug) return false;\n\tif (prevProps.onClose !== nextProps.onClose) return false;\n\tif (prevProps.onSuccess !== nextProps.onSuccess) return false;\n\tif (prevProps.onDelete !== nextProps.onDelete) return false;\n\tif (prevProps.onFormReady !== nextProps.onFormReady) return false;\n\treturn true;\n};\n\nconst EditPostFormComponent = ({\n\tpostSlug,\n\tonClose,\n\tonSuccess,\n\tonDelete,\n\tonFormReady,\n}: EditPostFormProps) => {\n\tconst [featuredImageUploading, setFeaturedImageUploading] = useState(false);\n\tconst [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\t// const { uploadImage } = useBlogContext()\n\n\tconst { post } = useSuspensePost(postSlug);\n\tconst notify = useNotify();\n\n\tconst schema = CustomPostUpdateSchema;\n\n\ttype EditPostFormValues = z.input;\n\n\tconst resourceForm = usePostForm({\n\t\taction: \"edit\",\n\t\t// Record comes from the suspense hook above — skips useForm's own fetch\n\t\trecord: post,\n\t\tsuccessMessage: localization.BLOG_FORMS_TOAST_UPDATE_SUCCESS,\n\t\tdefaults: (record) =>\n\t\t\t(record\n\t\t\t\t? {\n\t\t\t\t\t\ttitle: record.title,\n\t\t\t\t\t\tcontent: record.content,\n\t\t\t\t\t\texcerpt: record.excerpt,\n\t\t\t\t\t\tslug: record.slug,\n\t\t\t\t\t\tpublished: record.published,\n\t\t\t\t\t\timage: record.image || \"\",\n\t\t\t\t\t\ttags: record.tags.map((tag) => ({\n\t\t\t\t\t\t\tid: tag.id,\n\t\t\t\t\t\t\tname: tag.name,\n\t\t\t\t\t\t\tslug: tag.slug,\n\t\t\t\t\t\t})),\n\t\t\t\t\t}\n\t\t\t\t: {}) as EditPostFormValues,\n\t\ttoUpdateVars: (data, record) => ({\n\t\t\tid: (record as SerializedPost).id,\n\t\t\tdata: {\n\t\t\t\tid: (record as SerializedPost).id,\n\t\t\t\ttitle: data.title,\n\t\t\t\tcontent: data.content,\n\t\t\t\texcerpt: data.excerpt ?? \"\",\n\t\t\t\tslug: data.slug,\n\t\t\t\tpublished: data.published ?? false,\n\t\t\t\tpublishedAt:\n\t\t\t\t\tdata.published && !record?.published\n\t\t\t\t\t\t? new Date()\n\t\t\t\t\t\t: record?.publishedAt\n\t\t\t\t\t\t\t? new Date(record.publishedAt)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\timage: data.image,\n\t\t\t\ttags: data.tags || [],\n\t\t\t},\n\t\t}),\n\t\tonSuccess: (updatedPost) => {\n\t\t\t// Navigate only after mutation (including invalidation) completes\n\t\t\tonSuccess({\n\t\t\t\tslug: updatedPost?.slug ?? \"\",\n\t\t\t\tpublished: updatedPost?.published ?? false,\n\t\t\t});\n\t\t},\n\t});\n\n\tconst { mutateAsync: deletePost, isPending: isDeletingPost } =\n\t\tuseDeletePost();\n\n\tconst onSubmit = async (data: EditPostFormValues) => {\n\t\tawait resourceForm.submit(data);\n\t};\n\n\tconst handleDelete = async () => {\n\t\tif (!post?.id) return;\n\n\t\ttry {\n\t\t\tawait deletePost({ id: post.id });\n\t\t} catch (error) {\n\t\t\tnotify.error(\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? error.message\n\t\t\t\t\t: localization.BLOG_FORMS_TOAST_DELETE_FAILURE,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tnotify.success(localization.BLOG_FORMS_TOAST_DELETE_SUCCESS);\n\t\tsetDeleteDialogOpen(false);\n\n\t\t// Call onDelete callback if provided, otherwise use onClose\n\t\tif (onDelete) {\n\t\t\tonDelete();\n\t\t} else {\n\t\t\tonClose();\n\t\t}\n\t};\n\n\tconst form = useForm>({\n\t\tresolver: zodResolver(schema),\n\t\tdefaultValues: {\n\t\t\ttitle: \"\",\n\t\t\tcontent: \"\",\n\t\t\texcerpt: \"\",\n\t\t\tslug: \"\",\n\t\t\tpublished: false,\n\t\t\timage: \"\",\n\t\t\ttags: [],\n\t\t},\n\t\tvalues: resourceForm.defaultValues as z.input,\n\t});\n\n\t// Server-side Zod validation failures land on the matching form fields\n\tuseServerFieldErrors(form, resourceForm.fieldErrors);\n\tconst hasFieldErrors = Object.keys(resourceForm.fieldErrors).length > 0;\n\n\t// Expose form instance to parent for AI context integration\n\tuseEffect(() => {\n\t\tonFormReady?.(form);\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, []);\n\n\tif (!post) {\n\t\treturn ;\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_BUTTON}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_TITLE}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_DESCRIPTION}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_CANCEL}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\tvoid handleDelete();\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tdisabled={isDeletingPost}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isDeletingPost\n\t\t\t\t\t\t\t\t\t? localization.BLOG_FORMS_DELETE_PENDING\n\t\t\t\t\t\t\t\t\t: localization.BLOG_FORMS_DELETE_DIALOG_CONFIRM}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\n\t);\n};\n\nexport const EditPostForm = memo(\n\tEditPostFormComponent,\n\teditPostFormPropsAreEqual,\n);\n", "target": "src/components/btst/blog/client/components/forms/post-forms.tsx" }, { @@ -334,7 +334,7 @@ { "path": "btst/blog/client/localization/blog-forms.ts", "type": "registry:lib", - "content": "export const BLOG_FORMS = {\n\tBLOG_FORMS_TITLE_LABEL: \"Title\",\n\tBLOG_FORMS_REQUIRED_ASTERISK: \" *\",\n\tBLOG_FORMS_TITLE_PLACEHOLDER: \"Enter your post title...\",\n\n\tBLOG_FORMS_SLUG_LABEL: \"Slug\",\n\tBLOG_FORMS_SLUG_PLACEHOLDER: \"url-friendly-slug\",\n\n\tBLOG_FORMS_EXCERPT_LABEL: \"Excerpt\",\n\tBLOG_FORMS_EXCERPT_PLACEHOLDER: \"Brief summary of your post...\",\n\n\tBLOG_FORMS_TAGS_LABEL: \"Tags\",\n\tBLOG_FORMS_TAGS_PLACEHOLDER: \"Enter your post tags...\",\n\n\tBLOG_FORMS_CONTENT_LABEL: \"Content\",\n\n\tBLOG_FORMS_PUBLISHED_LABEL: \"Published\",\n\tBLOG_FORMS_PUBLISHED_DESCRIPTION: \"Toggle to publish immediately\",\n\n\tBLOG_FORMS_SUBMIT_CREATE_IDLE: \"Create Post\",\n\tBLOG_FORMS_SUBMIT_CREATE_PENDING: \"Creating...\",\n\tBLOG_FORMS_SUBMIT_UPDATE_IDLE: \"Update Post\",\n\tBLOG_FORMS_SUBMIT_UPDATE_PENDING: \"Updating...\",\n\tBLOG_FORMS_CANCEL_BUTTON: \"Cancel\",\n\n\tBLOG_FORMS_TOAST_CREATE_SUCCESS: \"Post created successfully\",\n\tBLOG_FORMS_TOAST_UPDATE_SUCCESS: \"Post updated successfully\",\n\tBLOG_FORMS_TOAST_DELETE_SUCCESS: \"Post deleted successfully\",\n\tBLOG_FORMS_LOADING_POST: \"Loading post...\",\n\n\t// Delete post\n\tBLOG_FORMS_DELETE_BUTTON: \"Delete Post\",\n\tBLOG_FORMS_DELETE_DIALOG_TITLE: \"Delete Post\",\n\tBLOG_FORMS_DELETE_DIALOG_DESCRIPTION:\n\t\t\"Are you sure you want to delete this post? This action cannot be undone.\",\n\tBLOG_FORMS_DELETE_DIALOG_CANCEL: \"Cancel\",\n\tBLOG_FORMS_DELETE_DIALOG_CONFIRM: \"Delete\",\n\tBLOG_FORMS_DELETE_PENDING: \"Deleting...\",\n\n\t// Markdown editor\n\tBLOG_FORMS_EDITOR_PLACEHOLDER: \"Write something...\",\n\n\t// Featured image field\n\tBLOG_FORMS_FEATURED_IMAGE_LABEL: \"Image\",\n\tBLOG_FORMS_FEATURED_IMAGE_REQUIRED_ASTERISK: \" *\",\n\tBLOG_FORMS_FEATURED_IMAGE_INPUT_PLACEHOLDER: \"Image URL or upload below...\",\n\tBLOG_FORMS_FEATURED_IMAGE_UPLOAD_BUTTON: \"Upload\",\n\tBLOG_FORMS_FEATURED_IMAGE_UPLOADING_BUTTON: \"Uploading...\",\n\tBLOG_FORMS_FEATURED_IMAGE_UPLOADING_TEXT: \"Uploading image...\",\n\tBLOG_FORMS_FEATURED_IMAGE_PREVIEW_ALT: \"Featured image preview\",\n\tBLOG_FORMS_FEATURED_IMAGE_ERROR_NOT_IMAGE: \"Please select an image file\",\n\tBLOG_FORMS_FEATURED_IMAGE_ERROR_TOO_LARGE: \"Image size must be less than 4MB\",\n\tBLOG_FORMS_FEATURED_IMAGE_TOAST_SUCCESS: \"Image uploaded successfully\",\n\tBLOG_FORMS_FEATURED_IMAGE_TOAST_FAILURE: \"Failed to upload image\",\n};\n", + "content": "export const BLOG_FORMS = {\n\tBLOG_FORMS_TITLE_LABEL: \"Title\",\n\tBLOG_FORMS_REQUIRED_ASTERISK: \" *\",\n\tBLOG_FORMS_TITLE_PLACEHOLDER: \"Enter your post title...\",\n\n\tBLOG_FORMS_SLUG_LABEL: \"Slug\",\n\tBLOG_FORMS_SLUG_PLACEHOLDER: \"url-friendly-slug\",\n\n\tBLOG_FORMS_EXCERPT_LABEL: \"Excerpt\",\n\tBLOG_FORMS_EXCERPT_PLACEHOLDER: \"Brief summary of your post...\",\n\n\tBLOG_FORMS_TAGS_LABEL: \"Tags\",\n\tBLOG_FORMS_TAGS_PLACEHOLDER: \"Enter your post tags...\",\n\n\tBLOG_FORMS_CONTENT_LABEL: \"Content\",\n\n\tBLOG_FORMS_PUBLISHED_LABEL: \"Published\",\n\tBLOG_FORMS_PUBLISHED_DESCRIPTION: \"Toggle to publish immediately\",\n\n\tBLOG_FORMS_SUBMIT_CREATE_IDLE: \"Create Post\",\n\tBLOG_FORMS_SUBMIT_CREATE_PENDING: \"Creating...\",\n\tBLOG_FORMS_SUBMIT_UPDATE_IDLE: \"Update Post\",\n\tBLOG_FORMS_SUBMIT_UPDATE_PENDING: \"Updating...\",\n\tBLOG_FORMS_CANCEL_BUTTON: \"Cancel\",\n\n\tBLOG_FORMS_TOAST_CREATE_SUCCESS: \"Post created successfully\",\n\tBLOG_FORMS_TOAST_UPDATE_SUCCESS: \"Post updated successfully\",\n\tBLOG_FORMS_TOAST_DELETE_SUCCESS: \"Post deleted successfully\",\n\tBLOG_FORMS_TOAST_DELETE_FAILURE: \"Failed to delete post\",\n\tBLOG_FORMS_LOADING_POST: \"Loading post...\",\n\n\t// Delete post\n\tBLOG_FORMS_DELETE_BUTTON: \"Delete Post\",\n\tBLOG_FORMS_DELETE_DIALOG_TITLE: \"Delete Post\",\n\tBLOG_FORMS_DELETE_DIALOG_DESCRIPTION:\n\t\t\"Are you sure you want to delete this post? This action cannot be undone.\",\n\tBLOG_FORMS_DELETE_DIALOG_CANCEL: \"Cancel\",\n\tBLOG_FORMS_DELETE_DIALOG_CONFIRM: \"Delete\",\n\tBLOG_FORMS_DELETE_PENDING: \"Deleting...\",\n\n\t// Markdown editor\n\tBLOG_FORMS_EDITOR_PLACEHOLDER: \"Write something...\",\n\n\t// Featured image field\n\tBLOG_FORMS_FEATURED_IMAGE_LABEL: \"Image\",\n\tBLOG_FORMS_FEATURED_IMAGE_REQUIRED_ASTERISK: \" *\",\n\tBLOG_FORMS_FEATURED_IMAGE_INPUT_PLACEHOLDER: \"Image URL or upload below...\",\n\tBLOG_FORMS_FEATURED_IMAGE_UPLOAD_BUTTON: \"Upload\",\n\tBLOG_FORMS_FEATURED_IMAGE_UPLOADING_BUTTON: \"Uploading...\",\n\tBLOG_FORMS_FEATURED_IMAGE_UPLOADING_TEXT: \"Uploading image...\",\n\tBLOG_FORMS_FEATURED_IMAGE_PREVIEW_ALT: \"Featured image preview\",\n\tBLOG_FORMS_FEATURED_IMAGE_ERROR_NOT_IMAGE: \"Please select an image file\",\n\tBLOG_FORMS_FEATURED_IMAGE_ERROR_TOO_LARGE: \"Image size must be less than 4MB\",\n\tBLOG_FORMS_FEATURED_IMAGE_TOAST_SUCCESS: \"Image uploaded successfully\",\n\tBLOG_FORMS_FEATURED_IMAGE_TOAST_FAILURE: \"Failed to upload image\",\n};\n", "target": "src/components/btst/blog/client/localization/blog-forms.ts" }, { diff --git a/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx b/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx index 1f25a55a6..4d53cce69 100644 --- a/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx +++ b/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx @@ -19,8 +19,11 @@ import { Input } from "@workspace/ui/components/input"; import { Switch } from "@workspace/ui/components/switch"; import { Textarea } from "@workspace/ui/components/textarea"; -import { useSuspensePost, useDeletePost } from "../../hooks/blog-hooks"; -import { blog } from "../../hooks/blog-resource"; +import { + useSuspensePost, + useDeletePost, + usePostForm, +} from "../../hooks/blog-hooks"; import { slugify } from "../../../utils"; import type { SerializedPost } from "../../../types"; import { @@ -375,10 +378,7 @@ const AddPostFormComponent = ({ type AddPostFormValues = z.input; - const resourceForm = blog.posts.useForm< - AddPostFormValues, - SerializedPost | null - >({ + const resourceForm = usePostForm({ action: "create", successMessage: localization.BLOG_FORMS_TOAST_CREATE_SUCCESS, toCreateVars: (data) => ({ @@ -493,10 +493,7 @@ const EditPostFormComponent = ({ type EditPostFormValues = z.input; - const resourceForm = blog.posts.useForm< - EditPostFormValues, - SerializedPost | null - >({ + const resourceForm = usePostForm({ action: "edit", // Record comes from the suspense hook above — skips useForm's own fetch record: post, diff --git a/packages/stack/src/plugins/blog/client/hooks/blog-hooks.tsx b/packages/stack/src/plugins/blog/client/hooks/blog-hooks.tsx index ceb2f5ae8..0ed3d3445 100644 --- a/packages/stack/src/plugins/blog/client/hooks/blog-hooks.tsx +++ b/packages/stack/src/plugins/blog/client/hooks/blog-hooks.tsx @@ -2,6 +2,10 @@ import { useEffect, useRef } from "react"; import { z } from "zod"; +import type { + ResourceFormConfig, + ResourceFormResult, +} from "@btst/stack/plugins/client/hooks"; import type { SerializedPost, SerializedTag } from "../../types"; import type { createPostSchema, updatePostSchema } from "../../schemas"; import { useDebounce } from "./use-debounce"; @@ -216,6 +220,21 @@ export function useSuspenseTags(): { }; } +/** + * Form lifecycle hook for creating/editing posts, built on the core + * resource `useForm`: submits the right mutation, awaits invalidation, + * notifies, redirects, and maps server validation issues to `fieldErrors`. + */ +export function usePostForm( + config: ResourceFormConfig< + TValues, + SerializedPost | null, + SerializedPost | null + >, +): ResourceFormResult { + return blog.posts.useForm(config); +} + /** Create a new post */ export function useCreatePost() { return blog.posts.create.use(); From 1d09b921e89068b2069a18e8c092ba2fd243dc01 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Sun, 5 Jul 2026 22:16:07 +0000 Subject: [PATCH 023/380] fix: clear stale server field errors on resubmit in post forms Co-authored-by: Cursor --- packages/stack/registry/btst-blog.json | 2 +- .../client/components/forms/post-forms.tsx | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/stack/registry/btst-blog.json b/packages/stack/registry/btst-blog.json index 215287474..28379d4f3 100644 --- a/packages/stack/registry/btst-blog.json +++ b/packages/stack/registry/btst-blog.json @@ -82,7 +82,7 @@ { "path": "btst/blog/client/components/forms/post-forms.tsx", "type": "registry:component", - "content": "\"use client\";\nimport {\n\tcreatePostSchema as PostCreateSchema,\n\tupdatePostSchema as PostUpdateSchema,\n} from \"../../../schemas\";\n\nimport { Button } from \"@/components/ui/button\";\n\nimport {\n\tForm,\n\tFormControl,\n\tFormDescription,\n\tFormField,\n\tFormItem,\n\tFormLabel,\n\tFormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\n\nimport { Switch } from \"@/components/ui/switch\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport {\n\tuseSuspensePost,\n\tuseDeletePost,\n\tusePostForm,\n} from \"@btst/stack/plugins/blog/client/hooks\";\nimport { slugify } from \"../../../utils\";\nimport type { SerializedPost } from \"../../../types\";\nimport {\n\tAlertDialog,\n\tAlertDialogAction,\n\tAlertDialogCancel,\n\tAlertDialogContent,\n\tAlertDialogDescription,\n\tAlertDialogFooter,\n\tAlertDialogHeader,\n\tAlertDialogTitle,\n\tAlertDialogTrigger,\n} from \"@/components/ui/alert-dialog\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { Loader2 } from \"lucide-react\";\nimport { lazy, memo, Suspense, useEffect, useState } from \"react\";\nimport {\n\ttype FieldPath,\n\ttype FieldValues,\n\ttype SubmitHandler,\n\ttype UseFormReturn,\n\tuseForm,\n} from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { FeaturedImageField } from \"./image-field\";\n\nconst MarkdownEditor = lazy(() =>\n\timport(\"./markdown-editor-with-overrides\").then((module) => ({\n\t\tdefault: module.MarkdownEditorWithOverrides,\n\t})),\n);\nimport { BLOG_LOCALIZATION } from \"../../localization\";\nimport { useNotify, usePluginOverrides } from \"@btst/stack/context\";\nimport type { BlogPluginOverrides } from \"../../overrides\";\nimport { EmptyList } from \"../shared/empty-list\";\nimport { TagsMultiSelect } from \"./tags-multiselect\";\n\n/**\n * Applies server-side field validation errors (from `StackError.errors`)\n * onto react-hook-form field state.\n */\nfunction useServerFieldErrors(\n\tform: UseFormReturn,\n\tfieldErrors: Record,\n) {\n\tuseEffect(() => {\n\t\tfor (const [field, message] of Object.entries(fieldErrors)) {\n\t\t\tform.setError(field as FieldPath, {\n\t\t\t\ttype: \"server\",\n\t\t\t\tmessage: Array.isArray(message) ? message.join(\", \") : message,\n\t\t\t});\n\t\t}\n\t}, [fieldErrors, form]);\n}\n\ntype CommonPostFormValues = {\n\ttitle: string;\n\tcontent: string;\n\texcerpt?: string;\n\tslug?: string;\n\timage?: string;\n\tpublished?: boolean;\n\ttags?: Array<{ name: string } | { id: string; name: string; slug: string }>;\n};\n\nfunction PostFormBody({\n\tform,\n\tonSubmit,\n\tsubmitLabel,\n\tonCancel,\n\tdisabled,\n\terrorMessage,\n\tsetFeaturedImageUploading,\n\tinitialSlugTouched = false,\n}: {\n\tform: UseFormReturn;\n\tonSubmit: SubmitHandler;\n\tsubmitLabel: string;\n\tonCancel: () => void;\n\tdisabled: boolean;\n\terrorMessage?: string;\n\tsetFeaturedImageUploading: (uploading: boolean) => void;\n\tinitialSlugTouched?: boolean;\n}) {\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\tconst [slugTouched, setSlugTouched] = useState(initialSlugTouched);\n\tconst nameTitle = \"title\" as FieldPath;\n\tconst nameSlug = \"slug\" as FieldPath;\n\tconst nameExcerpt = \"excerpt\" as FieldPath;\n\tconst nameImage = \"image\" as FieldPath;\n\tconst nameTags = \"tags\" as FieldPath;\n\tconst nameContent = \"content\" as FieldPath;\n\tconst namePublished = \"published\" as FieldPath;\n\treturn (\n\t\t
\n\t\t\t\n\t\t\t\t{errorMessage && (\n\t\t\t\t\t
\n\t\t\t\t\t\t{errorMessage}\n\t\t\t\t\t
\n\t\t\t\t)}\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_TITLE_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\tconst newTitle = e.target.value;\n\t\t\t\t\t\t\t\t\t\tfield.onChange(e);\n\t\t\t\t\t\t\t\t\t\t// Auto-slugify title if slug is not yet set\n\t\t\t\t\t\t\t\t\t\tif (!slugTouched) {\n\t\t\t\t\t\t\t\t\t\t\t// @ts-expect-error - slugify returns string which is compatible with slug field type\n\t\t\t\t\t\t\t\t\t\t\tform.setValue(nameSlug, slugify(newTitle));\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t {\n\t\t\t\t\t\tconst currentTitle = form.getValues(nameTitle);\n\t\t\t\t\t\tconst autoGeneratedSlug = slugify(String(currentTitle ?? \"\"));\n\t\t\t\t\t\tconst currentSlug = String(field.value ?? \"\");\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_SLUG_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\tconst newSlug = e.target.value;\n\t\t\t\t\t\t\t\t\t\t\tfield.onChange(e);\n\t\t\t\t\t\t\t\t\t\t\t// Only mark as touched if the user manually edited to something different from auto-generated\n\t\t\t\t\t\t\t\t\t\t\t// This allows auto-generation to continue if the slug matches what would be generated\n\t\t\t\t\t\t\t\t\t\t\tif (newSlug !== autoGeneratedSlug) {\n\t\t\t\t\t\t\t\t\t\t\t\tsetSlugTouched(true);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_EXCERPT_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{localization.BLOG_FORMS_TAGS_LABEL}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_CONTENT_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\tfield.onChange(content);\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_PUBLISHED_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_PUBLISHED_DESCRIPTION}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{localization.BLOG_FORMS_CANCEL_BUTTON}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\n\t\t\n\t);\n}\n\nconst CustomPostCreateSchema = PostCreateSchema.omit({\n\tcreatedAt: true,\n\tupdatedAt: true,\n\tpublishedAt: true,\n});\n\nconst CustomPostUpdateSchema = PostUpdateSchema.omit({\n\tid: true,\n\tcreatedAt: true,\n\tupdatedAt: true,\n\tpublishedAt: true,\n});\n\ntype AddPostFormProps = {\n\tonClose: () => void;\n\tonSuccess: (post: { published: boolean }) => void;\n\t/** Called once with the form instance so parent components can access form state */\n\tonFormReady?: (\n\t\tform: UseFormReturn>,\n\t) => void;\n};\n\nconst addPostFormPropsAreEqual = (\n\tprevProps: AddPostFormProps,\n\tnextProps: AddPostFormProps,\n): boolean => {\n\tif (prevProps.onClose !== nextProps.onClose) return false;\n\tif (prevProps.onSuccess !== nextProps.onSuccess) return false;\n\tif (prevProps.onFormReady !== nextProps.onFormReady) return false;\n\treturn true;\n};\n\nconst AddPostFormComponent = ({\n\tonClose,\n\tonSuccess,\n\tonFormReady,\n}: AddPostFormProps) => {\n\tconst [featuredImageUploading, setFeaturedImageUploading] = useState(false);\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\n\t// const { uploadImage } = useBlogContext()\n\n\tconst schema = CustomPostCreateSchema;\n\n\ttype AddPostFormValues = z.input;\n\n\tconst resourceForm = usePostForm({\n\t\taction: \"create\",\n\t\tsuccessMessage: localization.BLOG_FORMS_TOAST_CREATE_SUCCESS,\n\t\ttoCreateVars: (data) => ({\n\t\t\ttitle: data.title,\n\t\t\tcontent: data.content,\n\t\t\texcerpt: data.excerpt ?? \"\",\n\t\t\t// Auto-generate slug from title if not provided\n\t\t\tslug: data.slug || slugify(data.title),\n\t\t\tpublished: data.published ?? false,\n\t\t\tpublishedAt: data.published ? new Date() : undefined,\n\t\t\timage: data.image,\n\t\t\ttags: data.tags || [],\n\t\t}),\n\t\tonSuccess: (createdPost) => {\n\t\t\t// Navigate only after mutation (including invalidation) completes\n\t\t\tonSuccess({ published: createdPost?.published ?? false });\n\t\t},\n\t});\n\n\tconst onSubmit = async (data: AddPostFormValues) => {\n\t\tawait resourceForm.submit(data);\n\t};\n\n\t// For compatibility with resolver types that require certain required fields,\n\t// cast the generics to the exact inferred input type to avoid mismatch on optional slug\n\tconst form = useForm>({\n\t\tresolver: zodResolver(schema),\n\t\tdefaultValues: {\n\t\t\ttitle: \"\",\n\t\t\tcontent: \"\",\n\t\t\texcerpt: \"\",\n\t\t\tslug: undefined,\n\t\t\tpublished: false,\n\t\t\timage: \"\",\n\t\t\ttags: [],\n\t\t},\n\t});\n\n\t// Server-side Zod validation failures land on the matching form fields\n\tuseServerFieldErrors(form, resourceForm.fieldErrors);\n\tconst hasFieldErrors = Object.keys(resourceForm.fieldErrors).length > 0;\n\n\t// Expose form instance to parent for AI context integration\n\tuseEffect(() => {\n\t\tonFormReady?.(form);\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, []);\n\n\treturn (\n\t\t\n\t);\n};\n\nexport const AddPostForm = memo(AddPostFormComponent, addPostFormPropsAreEqual);\n\ntype EditPostFormProps = {\n\tpostSlug: string;\n\tonClose: () => void;\n\tonSuccess: (post: { slug: string; published: boolean }) => void;\n\tonDelete?: () => void;\n\t/** Called once with the form instance so parent components can access form state */\n\tonFormReady?: (\n\t\tform: UseFormReturn>,\n\t) => void;\n};\n\nconst editPostFormPropsAreEqual = (\n\tprevProps: EditPostFormProps,\n\tnextProps: EditPostFormProps,\n): boolean => {\n\tif (prevProps.postSlug !== nextProps.postSlug) return false;\n\tif (prevProps.onClose !== nextProps.onClose) return false;\n\tif (prevProps.onSuccess !== nextProps.onSuccess) return false;\n\tif (prevProps.onDelete !== nextProps.onDelete) return false;\n\tif (prevProps.onFormReady !== nextProps.onFormReady) return false;\n\treturn true;\n};\n\nconst EditPostFormComponent = ({\n\tpostSlug,\n\tonClose,\n\tonSuccess,\n\tonDelete,\n\tonFormReady,\n}: EditPostFormProps) => {\n\tconst [featuredImageUploading, setFeaturedImageUploading] = useState(false);\n\tconst [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\t// const { uploadImage } = useBlogContext()\n\n\tconst { post } = useSuspensePost(postSlug);\n\tconst notify = useNotify();\n\n\tconst schema = CustomPostUpdateSchema;\n\n\ttype EditPostFormValues = z.input;\n\n\tconst resourceForm = usePostForm({\n\t\taction: \"edit\",\n\t\t// Record comes from the suspense hook above — skips useForm's own fetch\n\t\trecord: post,\n\t\tsuccessMessage: localization.BLOG_FORMS_TOAST_UPDATE_SUCCESS,\n\t\tdefaults: (record) =>\n\t\t\t(record\n\t\t\t\t? {\n\t\t\t\t\t\ttitle: record.title,\n\t\t\t\t\t\tcontent: record.content,\n\t\t\t\t\t\texcerpt: record.excerpt,\n\t\t\t\t\t\tslug: record.slug,\n\t\t\t\t\t\tpublished: record.published,\n\t\t\t\t\t\timage: record.image || \"\",\n\t\t\t\t\t\ttags: record.tags.map((tag) => ({\n\t\t\t\t\t\t\tid: tag.id,\n\t\t\t\t\t\t\tname: tag.name,\n\t\t\t\t\t\t\tslug: tag.slug,\n\t\t\t\t\t\t})),\n\t\t\t\t\t}\n\t\t\t\t: {}) as EditPostFormValues,\n\t\ttoUpdateVars: (data, record) => ({\n\t\t\tid: (record as SerializedPost).id,\n\t\t\tdata: {\n\t\t\t\tid: (record as SerializedPost).id,\n\t\t\t\ttitle: data.title,\n\t\t\t\tcontent: data.content,\n\t\t\t\texcerpt: data.excerpt ?? \"\",\n\t\t\t\tslug: data.slug,\n\t\t\t\tpublished: data.published ?? false,\n\t\t\t\tpublishedAt:\n\t\t\t\t\tdata.published && !record?.published\n\t\t\t\t\t\t? new Date()\n\t\t\t\t\t\t: record?.publishedAt\n\t\t\t\t\t\t\t? new Date(record.publishedAt)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\timage: data.image,\n\t\t\t\ttags: data.tags || [],\n\t\t\t},\n\t\t}),\n\t\tonSuccess: (updatedPost) => {\n\t\t\t// Navigate only after mutation (including invalidation) completes\n\t\t\tonSuccess({\n\t\t\t\tslug: updatedPost?.slug ?? \"\",\n\t\t\t\tpublished: updatedPost?.published ?? false,\n\t\t\t});\n\t\t},\n\t});\n\n\tconst { mutateAsync: deletePost, isPending: isDeletingPost } =\n\t\tuseDeletePost();\n\n\tconst onSubmit = async (data: EditPostFormValues) => {\n\t\tawait resourceForm.submit(data);\n\t};\n\n\tconst handleDelete = async () => {\n\t\tif (!post?.id) return;\n\n\t\ttry {\n\t\t\tawait deletePost({ id: post.id });\n\t\t} catch (error) {\n\t\t\tnotify.error(\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? error.message\n\t\t\t\t\t: localization.BLOG_FORMS_TOAST_DELETE_FAILURE,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tnotify.success(localization.BLOG_FORMS_TOAST_DELETE_SUCCESS);\n\t\tsetDeleteDialogOpen(false);\n\n\t\t// Call onDelete callback if provided, otherwise use onClose\n\t\tif (onDelete) {\n\t\t\tonDelete();\n\t\t} else {\n\t\t\tonClose();\n\t\t}\n\t};\n\n\tconst form = useForm>({\n\t\tresolver: zodResolver(schema),\n\t\tdefaultValues: {\n\t\t\ttitle: \"\",\n\t\t\tcontent: \"\",\n\t\t\texcerpt: \"\",\n\t\t\tslug: \"\",\n\t\t\tpublished: false,\n\t\t\timage: \"\",\n\t\t\ttags: [],\n\t\t},\n\t\tvalues: resourceForm.defaultValues as z.input,\n\t});\n\n\t// Server-side Zod validation failures land on the matching form fields\n\tuseServerFieldErrors(form, resourceForm.fieldErrors);\n\tconst hasFieldErrors = Object.keys(resourceForm.fieldErrors).length > 0;\n\n\t// Expose form instance to parent for AI context integration\n\tuseEffect(() => {\n\t\tonFormReady?.(form);\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, []);\n\n\tif (!post) {\n\t\treturn ;\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_BUTTON}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_TITLE}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_DESCRIPTION}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_CANCEL}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\tvoid handleDelete();\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tdisabled={isDeletingPost}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isDeletingPost\n\t\t\t\t\t\t\t\t\t? localization.BLOG_FORMS_DELETE_PENDING\n\t\t\t\t\t\t\t\t\t: localization.BLOG_FORMS_DELETE_DIALOG_CONFIRM}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\n\t);\n};\n\nexport const EditPostForm = memo(\n\tEditPostFormComponent,\n\teditPostFormPropsAreEqual,\n);\n", + "content": "\"use client\";\nimport {\n\tcreatePostSchema as PostCreateSchema,\n\tupdatePostSchema as PostUpdateSchema,\n} from \"../../../schemas\";\n\nimport { Button } from \"@/components/ui/button\";\n\nimport {\n\tForm,\n\tFormControl,\n\tFormDescription,\n\tFormField,\n\tFormItem,\n\tFormLabel,\n\tFormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\n\nimport { Switch } from \"@/components/ui/switch\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport {\n\tuseSuspensePost,\n\tuseDeletePost,\n\tusePostForm,\n} from \"@btst/stack/plugins/blog/client/hooks\";\nimport { slugify } from \"../../../utils\";\nimport type { SerializedPost } from \"../../../types\";\nimport {\n\tAlertDialog,\n\tAlertDialogAction,\n\tAlertDialogCancel,\n\tAlertDialogContent,\n\tAlertDialogDescription,\n\tAlertDialogFooter,\n\tAlertDialogHeader,\n\tAlertDialogTitle,\n\tAlertDialogTrigger,\n} from \"@/components/ui/alert-dialog\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { Loader2 } from \"lucide-react\";\nimport { lazy, memo, Suspense, useEffect, useRef, useState } from \"react\";\nimport {\n\ttype FieldPath,\n\ttype FieldValues,\n\ttype SubmitHandler,\n\ttype UseFormReturn,\n\tuseForm,\n} from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { FeaturedImageField } from \"./image-field\";\n\nconst MarkdownEditor = lazy(() =>\n\timport(\"./markdown-editor-with-overrides\").then((module) => ({\n\t\tdefault: module.MarkdownEditorWithOverrides,\n\t})),\n);\nimport { BLOG_LOCALIZATION } from \"../../localization\";\nimport { useNotify, usePluginOverrides } from \"@btst/stack/context\";\nimport type { BlogPluginOverrides } from \"../../overrides\";\nimport { EmptyList } from \"../shared/empty-list\";\nimport { TagsMultiSelect } from \"./tags-multiselect\";\n\n/**\n * Applies server-side field validation errors (from `StackError.errors`)\n * onto react-hook-form field state, and clears previously applied server\n * errors that are no longer present (e.g. after a resubmit fails on\n * different fields).\n */\nfunction useServerFieldErrors(\n\tform: UseFormReturn,\n\tfieldErrors: Record,\n) {\n\tconst appliedFieldsRef = useRef([]);\n\n\tuseEffect(() => {\n\t\t// Clear stale server errors from fields that are no longer failing\n\t\tfor (const field of appliedFieldsRef.current) {\n\t\t\tif (field in fieldErrors) continue;\n\t\t\tconst { error } = form.getFieldState(field as FieldPath);\n\t\t\tif (error?.type === \"server\") {\n\t\t\t\tform.clearErrors(field as FieldPath);\n\t\t\t}\n\t\t}\n\t\tappliedFieldsRef.current = Object.keys(fieldErrors);\n\n\t\tfor (const [field, message] of Object.entries(fieldErrors)) {\n\t\t\tform.setError(field as FieldPath, {\n\t\t\t\ttype: \"server\",\n\t\t\t\tmessage: Array.isArray(message) ? message.join(\", \") : message,\n\t\t\t});\n\t\t}\n\t}, [fieldErrors, form]);\n}\n\ntype CommonPostFormValues = {\n\ttitle: string;\n\tcontent: string;\n\texcerpt?: string;\n\tslug?: string;\n\timage?: string;\n\tpublished?: boolean;\n\ttags?: Array<{ name: string } | { id: string; name: string; slug: string }>;\n};\n\nfunction PostFormBody({\n\tform,\n\tonSubmit,\n\tsubmitLabel,\n\tonCancel,\n\tdisabled,\n\terrorMessage,\n\tsetFeaturedImageUploading,\n\tinitialSlugTouched = false,\n}: {\n\tform: UseFormReturn;\n\tonSubmit: SubmitHandler;\n\tsubmitLabel: string;\n\tonCancel: () => void;\n\tdisabled: boolean;\n\terrorMessage?: string;\n\tsetFeaturedImageUploading: (uploading: boolean) => void;\n\tinitialSlugTouched?: boolean;\n}) {\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\tconst [slugTouched, setSlugTouched] = useState(initialSlugTouched);\n\tconst nameTitle = \"title\" as FieldPath;\n\tconst nameSlug = \"slug\" as FieldPath;\n\tconst nameExcerpt = \"excerpt\" as FieldPath;\n\tconst nameImage = \"image\" as FieldPath;\n\tconst nameTags = \"tags\" as FieldPath;\n\tconst nameContent = \"content\" as FieldPath;\n\tconst namePublished = \"published\" as FieldPath;\n\treturn (\n\t\t
\n\t\t\t\n\t\t\t\t{errorMessage && (\n\t\t\t\t\t
\n\t\t\t\t\t\t{errorMessage}\n\t\t\t\t\t
\n\t\t\t\t)}\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_TITLE_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\tconst newTitle = e.target.value;\n\t\t\t\t\t\t\t\t\t\tfield.onChange(e);\n\t\t\t\t\t\t\t\t\t\t// Auto-slugify title if slug is not yet set\n\t\t\t\t\t\t\t\t\t\tif (!slugTouched) {\n\t\t\t\t\t\t\t\t\t\t\t// @ts-expect-error - slugify returns string which is compatible with slug field type\n\t\t\t\t\t\t\t\t\t\t\tform.setValue(nameSlug, slugify(newTitle));\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t {\n\t\t\t\t\t\tconst currentTitle = form.getValues(nameTitle);\n\t\t\t\t\t\tconst autoGeneratedSlug = slugify(String(currentTitle ?? \"\"));\n\t\t\t\t\t\tconst currentSlug = String(field.value ?? \"\");\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_SLUG_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\tconst newSlug = e.target.value;\n\t\t\t\t\t\t\t\t\t\t\tfield.onChange(e);\n\t\t\t\t\t\t\t\t\t\t\t// Only mark as touched if the user manually edited to something different from auto-generated\n\t\t\t\t\t\t\t\t\t\t\t// This allows auto-generation to continue if the slug matches what would be generated\n\t\t\t\t\t\t\t\t\t\t\tif (newSlug !== autoGeneratedSlug) {\n\t\t\t\t\t\t\t\t\t\t\t\tsetSlugTouched(true);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_EXCERPT_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{localization.BLOG_FORMS_TAGS_LABEL}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_CONTENT_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_REQUIRED_ASTERISK}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\tfield.onChange(content);\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_PUBLISHED_LABEL}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_PUBLISHED_DESCRIPTION}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t/>\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{localization.BLOG_FORMS_CANCEL_BUTTON}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\n\t\t\n\t);\n}\n\nconst CustomPostCreateSchema = PostCreateSchema.omit({\n\tcreatedAt: true,\n\tupdatedAt: true,\n\tpublishedAt: true,\n});\n\nconst CustomPostUpdateSchema = PostUpdateSchema.omit({\n\tid: true,\n\tcreatedAt: true,\n\tupdatedAt: true,\n\tpublishedAt: true,\n});\n\ntype AddPostFormProps = {\n\tonClose: () => void;\n\tonSuccess: (post: { published: boolean }) => void;\n\t/** Called once with the form instance so parent components can access form state */\n\tonFormReady?: (\n\t\tform: UseFormReturn>,\n\t) => void;\n};\n\nconst addPostFormPropsAreEqual = (\n\tprevProps: AddPostFormProps,\n\tnextProps: AddPostFormProps,\n): boolean => {\n\tif (prevProps.onClose !== nextProps.onClose) return false;\n\tif (prevProps.onSuccess !== nextProps.onSuccess) return false;\n\tif (prevProps.onFormReady !== nextProps.onFormReady) return false;\n\treturn true;\n};\n\nconst AddPostFormComponent = ({\n\tonClose,\n\tonSuccess,\n\tonFormReady,\n}: AddPostFormProps) => {\n\tconst [featuredImageUploading, setFeaturedImageUploading] = useState(false);\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\n\t// const { uploadImage } = useBlogContext()\n\n\tconst schema = CustomPostCreateSchema;\n\n\ttype AddPostFormValues = z.input;\n\n\tconst resourceForm = usePostForm({\n\t\taction: \"create\",\n\t\tsuccessMessage: localization.BLOG_FORMS_TOAST_CREATE_SUCCESS,\n\t\ttoCreateVars: (data) => ({\n\t\t\ttitle: data.title,\n\t\t\tcontent: data.content,\n\t\t\texcerpt: data.excerpt ?? \"\",\n\t\t\t// Auto-generate slug from title if not provided\n\t\t\tslug: data.slug || slugify(data.title),\n\t\t\tpublished: data.published ?? false,\n\t\t\tpublishedAt: data.published ? new Date() : undefined,\n\t\t\timage: data.image,\n\t\t\ttags: data.tags || [],\n\t\t}),\n\t\tonSuccess: (createdPost) => {\n\t\t\t// Navigate only after mutation (including invalidation) completes\n\t\t\tonSuccess({ published: createdPost?.published ?? false });\n\t\t},\n\t});\n\n\tconst onSubmit = async (data: AddPostFormValues) => {\n\t\tawait resourceForm.submit(data);\n\t};\n\n\t// For compatibility with resolver types that require certain required fields,\n\t// cast the generics to the exact inferred input type to avoid mismatch on optional slug\n\tconst form = useForm>({\n\t\tresolver: zodResolver(schema),\n\t\tdefaultValues: {\n\t\t\ttitle: \"\",\n\t\t\tcontent: \"\",\n\t\t\texcerpt: \"\",\n\t\t\tslug: undefined,\n\t\t\tpublished: false,\n\t\t\timage: \"\",\n\t\t\ttags: [],\n\t\t},\n\t});\n\n\t// Server-side Zod validation failures land on the matching form fields\n\tuseServerFieldErrors(form, resourceForm.fieldErrors);\n\tconst hasFieldErrors = Object.keys(resourceForm.fieldErrors).length > 0;\n\n\t// Expose form instance to parent for AI context integration\n\tuseEffect(() => {\n\t\tonFormReady?.(form);\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, []);\n\n\treturn (\n\t\t\n\t);\n};\n\nexport const AddPostForm = memo(AddPostFormComponent, addPostFormPropsAreEqual);\n\ntype EditPostFormProps = {\n\tpostSlug: string;\n\tonClose: () => void;\n\tonSuccess: (post: { slug: string; published: boolean }) => void;\n\tonDelete?: () => void;\n\t/** Called once with the form instance so parent components can access form state */\n\tonFormReady?: (\n\t\tform: UseFormReturn>,\n\t) => void;\n};\n\nconst editPostFormPropsAreEqual = (\n\tprevProps: EditPostFormProps,\n\tnextProps: EditPostFormProps,\n): boolean => {\n\tif (prevProps.postSlug !== nextProps.postSlug) return false;\n\tif (prevProps.onClose !== nextProps.onClose) return false;\n\tif (prevProps.onSuccess !== nextProps.onSuccess) return false;\n\tif (prevProps.onDelete !== nextProps.onDelete) return false;\n\tif (prevProps.onFormReady !== nextProps.onFormReady) return false;\n\treturn true;\n};\n\nconst EditPostFormComponent = ({\n\tpostSlug,\n\tonClose,\n\tonSuccess,\n\tonDelete,\n\tonFormReady,\n}: EditPostFormProps) => {\n\tconst [featuredImageUploading, setFeaturedImageUploading] = useState(false);\n\tconst [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n\tconst { localization } = usePluginOverrides<\n\t\tBlogPluginOverrides,\n\t\tPartial\n\t>(\"blog\", {\n\t\tlocalization: BLOG_LOCALIZATION,\n\t});\n\t// const { uploadImage } = useBlogContext()\n\n\tconst { post } = useSuspensePost(postSlug);\n\tconst notify = useNotify();\n\n\tconst schema = CustomPostUpdateSchema;\n\n\ttype EditPostFormValues = z.input;\n\n\tconst resourceForm = usePostForm({\n\t\taction: \"edit\",\n\t\t// Record comes from the suspense hook above — skips useForm's own fetch\n\t\trecord: post,\n\t\tsuccessMessage: localization.BLOG_FORMS_TOAST_UPDATE_SUCCESS,\n\t\tdefaults: (record) =>\n\t\t\t(record\n\t\t\t\t? {\n\t\t\t\t\t\ttitle: record.title,\n\t\t\t\t\t\tcontent: record.content,\n\t\t\t\t\t\texcerpt: record.excerpt,\n\t\t\t\t\t\tslug: record.slug,\n\t\t\t\t\t\tpublished: record.published,\n\t\t\t\t\t\timage: record.image || \"\",\n\t\t\t\t\t\ttags: record.tags.map((tag) => ({\n\t\t\t\t\t\t\tid: tag.id,\n\t\t\t\t\t\t\tname: tag.name,\n\t\t\t\t\t\t\tslug: tag.slug,\n\t\t\t\t\t\t})),\n\t\t\t\t\t}\n\t\t\t\t: {}) as EditPostFormValues,\n\t\ttoUpdateVars: (data, record) => ({\n\t\t\tid: (record as SerializedPost).id,\n\t\t\tdata: {\n\t\t\t\tid: (record as SerializedPost).id,\n\t\t\t\ttitle: data.title,\n\t\t\t\tcontent: data.content,\n\t\t\t\texcerpt: data.excerpt ?? \"\",\n\t\t\t\tslug: data.slug,\n\t\t\t\tpublished: data.published ?? false,\n\t\t\t\tpublishedAt:\n\t\t\t\t\tdata.published && !record?.published\n\t\t\t\t\t\t? new Date()\n\t\t\t\t\t\t: record?.publishedAt\n\t\t\t\t\t\t\t? new Date(record.publishedAt)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\timage: data.image,\n\t\t\t\ttags: data.tags || [],\n\t\t\t},\n\t\t}),\n\t\tonSuccess: (updatedPost) => {\n\t\t\t// Navigate only after mutation (including invalidation) completes\n\t\t\tonSuccess({\n\t\t\t\tslug: updatedPost?.slug ?? \"\",\n\t\t\t\tpublished: updatedPost?.published ?? false,\n\t\t\t});\n\t\t},\n\t});\n\n\tconst { mutateAsync: deletePost, isPending: isDeletingPost } =\n\t\tuseDeletePost();\n\n\tconst onSubmit = async (data: EditPostFormValues) => {\n\t\tawait resourceForm.submit(data);\n\t};\n\n\tconst handleDelete = async () => {\n\t\tif (!post?.id) return;\n\n\t\ttry {\n\t\t\tawait deletePost({ id: post.id });\n\t\t} catch (error) {\n\t\t\tnotify.error(\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? error.message\n\t\t\t\t\t: localization.BLOG_FORMS_TOAST_DELETE_FAILURE,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tnotify.success(localization.BLOG_FORMS_TOAST_DELETE_SUCCESS);\n\t\tsetDeleteDialogOpen(false);\n\n\t\t// Call onDelete callback if provided, otherwise use onClose\n\t\tif (onDelete) {\n\t\t\tonDelete();\n\t\t} else {\n\t\t\tonClose();\n\t\t}\n\t};\n\n\tconst form = useForm>({\n\t\tresolver: zodResolver(schema),\n\t\tdefaultValues: {\n\t\t\ttitle: \"\",\n\t\t\tcontent: \"\",\n\t\t\texcerpt: \"\",\n\t\t\tslug: \"\",\n\t\t\tpublished: false,\n\t\t\timage: \"\",\n\t\t\ttags: [],\n\t\t},\n\t\tvalues: resourceForm.defaultValues as z.input,\n\t});\n\n\t// Server-side Zod validation failures land on the matching form fields\n\tuseServerFieldErrors(form, resourceForm.fieldErrors);\n\tconst hasFieldErrors = Object.keys(resourceForm.fieldErrors).length > 0;\n\n\t// Expose form instance to parent for AI context integration\n\tuseEffect(() => {\n\t\tonFormReady?.(form);\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, []);\n\n\tif (!post) {\n\t\treturn ;\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_BUTTON}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_TITLE}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_DESCRIPTION}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{localization.BLOG_FORMS_DELETE_DIALOG_CANCEL}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\tvoid handleDelete();\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tdisabled={isDeletingPost}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isDeletingPost\n\t\t\t\t\t\t\t\t\t? localization.BLOG_FORMS_DELETE_PENDING\n\t\t\t\t\t\t\t\t\t: localization.BLOG_FORMS_DELETE_DIALOG_CONFIRM}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\n\t);\n};\n\nexport const EditPostForm = memo(\n\tEditPostFormComponent,\n\teditPostFormPropsAreEqual,\n);\n", "target": "src/components/btst/blog/client/components/forms/post-forms.tsx" }, { diff --git a/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx b/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx index 4d53cce69..b647b90bd 100644 --- a/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx +++ b/packages/stack/src/plugins/blog/client/components/forms/post-forms.tsx @@ -40,7 +40,7 @@ import { import { zodResolver } from "@hookform/resolvers/zod"; import { Loader2 } from "lucide-react"; -import { lazy, memo, Suspense, useEffect, useState } from "react"; +import { lazy, memo, Suspense, useEffect, useRef, useState } from "react"; import { type FieldPath, type FieldValues, @@ -64,13 +64,27 @@ import { TagsMultiSelect } from "./tags-multiselect"; /** * Applies server-side field validation errors (from `StackError.errors`) - * onto react-hook-form field state. + * onto react-hook-form field state, and clears previously applied server + * errors that are no longer present (e.g. after a resubmit fails on + * different fields). */ function useServerFieldErrors( form: UseFormReturn, fieldErrors: Record, ) { + const appliedFieldsRef = useRef([]); + useEffect(() => { + // Clear stale server errors from fields that are no longer failing + for (const field of appliedFieldsRef.current) { + if (field in fieldErrors) continue; + const { error } = form.getFieldState(field as FieldPath); + if (error?.type === "server") { + form.clearErrors(field as FieldPath); + } + } + appliedFieldsRef.current = Object.keys(fieldErrors); + for (const [field, message] of Object.entries(fieldErrors)) { form.setError(field as FieldPath, { type: "server", From 110f1808411bc396ed79875a9c8422a0bb0b8262 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Mon, 6 Jul 2026 14:21:46 +0000 Subject: [PATCH 024/380] feat(blog): replace direct sonner toasts with useNotify in image field Co-authored-by: Cursor --- .../blog/client/components/forms/image-field.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/stack/src/plugins/blog/client/components/forms/image-field.tsx b/packages/stack/src/plugins/blog/client/components/forms/image-field.tsx index 8c553df8e..01d341d14 100644 --- a/packages/stack/src/plugins/blog/client/components/forms/image-field.tsx +++ b/packages/stack/src/plugins/blog/client/components/forms/image-field.tsx @@ -10,7 +10,6 @@ import { Input } from "@workspace/ui/components/input"; import { usePluginOverrides } from "@btst/stack/context"; import { Loader2, Upload } from "lucide-react"; import { useRef, useState } from "react"; -import { toast } from "sonner"; import type { BlogPluginOverrides } from "../../overrides"; import { BLOG_LOCALIZATION } from "../../localization"; @@ -27,6 +26,7 @@ export function FeaturedImageField({ }) { const fileInputRef = useRef(null); const [isUploading, setIsUploading] = useState(false); + const notify = useNotify(); const { uploadImage, @@ -73,12 +73,12 @@ export function FeaturedImageField({ if (!file) return; if (!file.type.startsWith("image/")) { - toast.error(localization.BLOG_FORMS_FEATURED_IMAGE_ERROR_NOT_IMAGE); + notify.error(localization.BLOG_FORMS_FEATURED_IMAGE_ERROR_NOT_IMAGE); return; } if (file.size > 4 * 1024 * 1024) { - toast.error(localization.BLOG_FORMS_FEATURED_IMAGE_ERROR_TOO_LARGE); + notify.error(localization.BLOG_FORMS_FEATURED_IMAGE_ERROR_TOO_LARGE); return; } @@ -87,11 +87,10 @@ export function FeaturedImageField({ setFeaturedImageUploading(true); const url = await uploadImage(file); onChange(url); - toast.success(localization.BLOG_FORMS_FEATURED_IMAGE_TOAST_SUCCESS); + notify.success(localization.BLOG_FORMS_FEATURED_IMAGE_TOAST_SUCCESS); } catch (error) { - toast.error(localization.BLOG_FORMS_FEATURED_IMAGE_TOAST_FAILURE); console.error("Failed to upload image:", error); - toast.error(localization.BLOG_FORMS_FEATURED_IMAGE_TOAST_FAILURE); + notify.error(localization.BLOG_FORMS_FEATURED_IMAGE_TOAST_FAILURE); } finally { setIsUploading(false); setFeaturedImageUploading(false); From c7374223231a5270590dd66514567f99ddb96857 Mon Sep 17 00:00:00 2001 From: olliethedev <5933733+olliethedev@users.noreply.github.com> Date: Mon, 6 Jul 2026 14:35:03 +0000 Subject: [PATCH 025/380] feat(blog): convert UI strings to useTranslate with override-wins localization fallback Every user-visible string resolves as localization?.KEY ?? t("blog..", "English default"): an app-provided overrides.localization still wins byte-for-byte, otherwise the string goes through the i18n provider (or its literal English default without one). Client resolver schemas now carry translatable required-field messages; server-side schemas.ts is unchanged. Adds the i18n key reference docs page with the blog key catalog. Co-authored-by: Cursor --- docs/content/docs/i18n.mdx | 116 ++++++++++ docs/content/docs/meta.json | 1 + .../client/components/forms/image-field.tsx | 105 ++++++--- .../forms/markdown-editor-with-overrides.tsx | 14 +- .../client/components/forms/post-forms.tsx | 217 +++++++++++++----- .../components/forms/tags-multiselect.tsx | 7 +- .../blog/client/components/pages/404-page.tsx | 23 +- .../pages/edit-post-page.internal.tsx | 25 +- .../components/pages/home-page.internal.tsx | 17 +- .../pages/new-post-page.internal.tsx | 25 +- .../components/pages/post-page.internal.tsx | 15 +- .../components/pages/tag-page.internal.tsx | 39 +++- .../shared/collapsible-tag-list.tsx | 27 +-- .../components/shared/default-error.tsx | 22 +- .../client/components/shared/on-this-page.tsx | 29 ++- .../client/components/shared/post-card.tsx | 19 +- .../components/shared/post-navigation.tsx | 11 +- .../client/components/shared/posts-list.tsx | 43 ++-- .../shared/recent-posts-carousel.tsx | 15 +- .../client/components/shared/search-modal.tsx | 21 +- 20 files changed, 553 insertions(+), 238 deletions(-) create mode 100644 docs/content/docs/i18n.mdx diff --git a/docs/content/docs/i18n.mdx b/docs/content/docs/i18n.mdx new file mode 100644 index 000000000..44295b8f8 --- /dev/null +++ b/docs/content/docs/i18n.mdx @@ -0,0 +1,116 @@ +--- +title: i18n Keys +description: Translation key conventions and the reference catalog of plugin i18n keys +--- + +Plugins render every user-visible string through `useTranslate()` from `@btst/stack/context`: + +```tsx +const t = useTranslate(); + +t("blog.forms.titleLabel", "Title"); +t("blog.list.tagPageTitle", "{{tag}} Posts", { tag: tag.name }); +``` + +Without an `i18n` provider on `StackProvider`, `t()` returns the English default (with `{{param}}` interpolation), so apps with no provider behave exactly as before. With a provider, your `translate(key, defaultValue, params)` implementation decides what to render. + +## Key conventions + +- Keys are namespaced `..`, e.g. `blog.forms.titleLabel`. Areas group related UI (`common`, `list`, `card`, `post`, `forms`, `search`, ...). +- The second argument is always a **string literal** English default, so the key catalog can be regenerated mechanically: + +```bash +pnpm exec tsx packages/stack/scripts/extract-i18n-keys.ts +``` + +- Parameters use `{{param}}` interpolation in the default value. +- Plugins that support a legacy per-plugin `localization` override object resolve it with higher precedence than `t()`: + +```tsx +localization?.BLOG_FORMS_TITLE_LABEL ?? t("blog.forms.titleLabel", "Title"); +``` + +An app-provided `localization` override wins byte-for-byte; otherwise the string goes through the i18n provider. + +## Blog + +| Key | Default | +| --- | --- | +| `blog.card.draftBadge` | Draft | +| `blog.common.genericErrorMessage` | An unexpected error occurred. | +| `blog.common.genericErrorTitle` | Something went wrong | +| `blog.common.pageNotFoundDescription` | The page you are looking for does not exist. | +| `blog.common.pageNotFoundTitle` | Not Found | +| `blog.common.tagsShowAll` | Show all tags | +| `blog.common.tagsShowLess` | Show fewer tags | +| `blog.forms.cancelButton` | Cancel | +| `blog.forms.contentLabel` | Content | +| `blog.forms.deleteButton` | Delete Post | +| `blog.forms.deleteDialogCancel` | Cancel | +| `blog.forms.deleteDialogConfirm` | Delete | +| `blog.forms.deleteDialogDescription` | Are you sure you want to delete this post? This action cannot be undone. | +| `blog.forms.deleteDialogTitle` | Delete Post | +| `blog.forms.deletePending` | Deleting... | +| `blog.forms.editorPlaceholder` | Write something... | +| `blog.forms.excerptLabel` | Excerpt | +| `blog.forms.excerptPlaceholder` | Brief summary of your post... | +| `blog.forms.featuredImageErrorNotImage` | Please select an image file | +| `blog.forms.featuredImageErrorTooLarge` | Image size must be less than 4MB | +| `blog.forms.featuredImageInputPlaceholder` | Image URL or upload below... | +| `blog.forms.featuredImageLabel` | Image | +| `blog.forms.featuredImagePreviewAlt` | Featured image preview | +| `blog.forms.featuredImageRequiredAsterisk` |  * | +| `blog.forms.featuredImageToastFailure` | Failed to upload image | +| `blog.forms.featuredImageToastSuccess` | Image uploaded successfully | +| `blog.forms.featuredImageUploadButton` | Upload | +| `blog.forms.featuredImageUploadingButton` | Uploading... | +| `blog.forms.featuredImageUploadingText` | Uploading image... | +| `blog.forms.publishedDescription` | Toggle to publish immediately | +| `blog.forms.publishedLabel` | Published | +| `blog.forms.requiredAsterisk` |  * | +| `blog.forms.slugLabel` | Slug | +| `blog.forms.slugPlaceholder` | url-friendly-slug | +| `blog.forms.submitCreateIdle` | Create Post | +| `blog.forms.submitCreatePending` | Creating... | +| `blog.forms.submitUpdateIdle` | Update Post | +| `blog.forms.submitUpdatePending` | Updating... | +| `blog.forms.tagsLabel` | Tags | +| `blog.forms.tagsPlaceholder` | Enter your post tags... | +| `blog.forms.tagsSearchPlaceholder` | Search or create tags... | +| `blog.forms.titleLabel` | Title | +| `blog.forms.titlePlaceholder` | Enter your post title... | +| `blog.forms.toastCreateSuccess` | Post created successfully | +| `blog.forms.toastDeleteFailure` | Failed to delete post | +| `blog.forms.toastDeleteSuccess` | Post deleted successfully | +| `blog.forms.toastUpdateSuccess` | Post updated successfully | +| `blog.forms.validation.contentRequired` | Content is required | +| `blog.forms.validation.excerptRequired` | Excerpt is required | +| `blog.forms.validation.slugRequired` | Slug is required | +| `blog.forms.validation.titleRequired` | Title is required | +| `blog.list.draftsTitle` | Draft Posts | +| `blog.list.empty` | There are no posts here yet. | +| `blog.list.loadMore` | Load more posts | +| `blog.list.loadingMore` | Loading more... | +| `blog.list.searchButton` | Search Posts | +| `blog.list.searchEmpty` | No blog posts found. | +| `blog.list.searchPlaceholder` | Search Blog Posts... | +| `blog.list.tagNotFound` | Tag not found | +| `blog.list.tagNotFoundDescription` | The tag you are looking for does not exist. | +| `blog.list.tagPageDescription` | Browse all posts with this tag | +| `blog.list.tagPageTitle` | \{\{tag\}\} Posts | +| `blog.list.title` | Blog Posts | +| `blog.post.addDescription` | Create a new blog post. | +| `blog.post.addTitle` | Add New Post | +| `blog.post.editDescription` | Update your blog post. | +| `blog.post.editTitle` | Edit Post | +| `blog.post.keepReading` | Keep Reading | +| `blog.post.next` | Next | +| `blog.post.onThisPage` | In This Post | +| `blog.post.previous` | Previous | +| `blog.post.viewAll` | View all | +| `blog.search.button` | Search | +| `blog.search.empty` | No results found. | +| `blog.search.placeholder` | Type to search... | +| `blog.search.searching` | Searching... | + +Other plugins adopt the same convention as their phase-2 sweeps land. diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json index 50f9178e7..8acc83368 100644 --- a/docs/content/docs/meta.json +++ b/docs/content/docs/meta.json @@ -27,6 +27,7 @@ "databases/adapters", "---[BookOpenCheck]Concepts---", "auth", + "i18n", "cli", "api-reference", "standalone-components", diff --git a/packages/stack/src/plugins/blog/client/components/forms/image-field.tsx b/packages/stack/src/plugins/blog/client/components/forms/image-field.tsx index 01d341d14..b3c935c27 100644 --- a/packages/stack/src/plugins/blog/client/components/forms/image-field.tsx +++ b/packages/stack/src/plugins/blog/client/components/forms/image-field.tsx @@ -7,11 +7,14 @@ import { FormMessage, } from "@workspace/ui/components/form"; import { Input } from "@workspace/ui/components/input"; -import { usePluginOverrides } from "@btst/stack/context"; +import { + useNotify, + usePluginOverrides, + useTranslate, +} from "@btst/stack/context"; import { Loader2, Upload } from "lucide-react"; import { useRef, useState } from "react"; import type { BlogPluginOverrides } from "../../overrides"; -import { BLOG_LOCALIZATION } from "../../localization"; export function FeaturedImageField({ isRequired, @@ -27,32 +30,36 @@ export function FeaturedImageField({ const fileInputRef = useRef(null); const [isUploading, setIsUploading] = useState(false); const notify = useNotify(); + const t = useTranslate(); const { uploadImage, Image, localization, imageInputField: ImageInput, - } = usePluginOverrides>( - "blog", - { localization: BLOG_LOCALIZATION }, - ); + } = usePluginOverrides("blog"); const ImageComponent = Image ? Image : DefaultImage; + const label = ( + + {localization?.BLOG_FORMS_FEATURED_IMAGE_LABEL ?? + t("blog.forms.featuredImageLabel", "Image")} + {isRequired && ( + + {" "} + {localization?.BLOG_FORMS_FEATURED_IMAGE_REQUIRED_ASTERISK ?? + t("blog.forms.featuredImageRequiredAsterisk", " *")} + + )} + + ); + // When a custom imageInput component is provided via overrides, delegate to it. if (ImageInput) { return ( - - {localization.BLOG_FORMS_FEATURED_IMAGE_LABEL} - {isRequired && ( - - {" "} - {localization.BLOG_FORMS_FEATURED_IMAGE_REQUIRED_ASTERISK} - - )} - + {label} 4 * 1024 * 1024) { - notify.error(localization.BLOG_FORMS_FEATURED_IMAGE_ERROR_TOO_LARGE); + notify.error( + localization?.BLOG_FORMS_FEATURED_IMAGE_ERROR_TOO_LARGE ?? + t( + "blog.forms.featuredImageErrorTooLarge", + "Image size must be less than 4MB", + ), + ); return; } @@ -87,10 +106,19 @@ export function FeaturedImageField({ setFeaturedImageUploading(true); const url = await uploadImage(file); onChange(url); - notify.success(localization.BLOG_FORMS_FEATURED_IMAGE_TOAST_SUCCESS); + notify.success( + localization?.BLOG_FORMS_FEATURED_IMAGE_TOAST_SUCCESS ?? + t( + "blog.forms.featuredImageToastSuccess", + "Image uploaded successfully", + ), + ); } catch (error) { console.error("Failed to upload image:", error); - notify.error(localization.BLOG_FORMS_FEATURED_IMAGE_TOAST_FAILURE); + notify.error( + localization?.BLOG_FORMS_FEATURED_IMAGE_TOAST_FAILURE ?? + t("blog.forms.featuredImageToastFailure", "Failed to upload image"), + ); } finally { setIsUploading(false); setFeaturedImageUploading(false); @@ -99,21 +127,17 @@ export function FeaturedImageField({ return ( - - {localization.BLOG_FORMS_FEATURED_IMAGE_LABEL} - {isRequired && ( - - {" "} - {localization.BLOG_FORMS_FEATURED_IMAGE_REQUIRED_ASTERISK} - - )} - + {label}
onChange(e.target.value)} @@ -128,12 +152,17 @@ export function FeaturedImageField({ {isUploading ? ( <> - {localization.BLOG_FORMS_FEATURED_IMAGE_UPLOADING_BUTTON} + {localization?.BLOG_FORMS_FEATURED_IMAGE_UPLOADING_BUTTON ?? + t( + "blog.forms.featuredImageUploadingButton", + "Uploading...", + )} ) : ( <> - {localization.BLOG_FORMS_FEATURED_IMAGE_UPLOAD_BUTTON} + {localization?.BLOG_FORMS_FEATURED_IMAGE_UPLOAD_BUTTON ?? + t("blog.forms.featuredImageUploadButton", "Upload")} )} @@ -148,14 +177,24 @@ export function FeaturedImageField({ {isUploading && (
- {localization.BLOG_FORMS_FEATURED_IMAGE_UPLOADING_TEXT} + {localization?.BLOG_FORMS_FEATURED_IMAGE_UPLOADING_TEXT ?? + t( + "blog.forms.featuredImageUploadingText", + "Uploading image...", + )}
)} {value && !isUploading && (
>( - "blog", - { localization: BLOG_LOCALIZATION }, - ); + } = usePluginOverrides("blog"); const insertImageRef = useRef<((url: string) => void) | null>(null); // Holds the Crepe-image-block `setUrl` callback while the picker is open. @@ -64,7 +61,10 @@ export function MarkdownEditorWithOverrides( default: module.MarkdownEditorWithOverrides, })), ); -import { BLOG_LOCALIZATION } from "../../localization"; -import { useNotify, usePluginOverrides } from "@btst/stack/context"; +import { + useNotify, + usePluginOverrides, + useTranslate, + type TranslateFn, +} from "@btst/stack/context"; import type { BlogPluginOverrides } from "../../overrides"; import { EmptyList } from "../shared/empty-list"; import { TagsMultiSelect } from "./tags-multiselect"; @@ -123,13 +135,12 @@ function PostFormBody({ setFeaturedImageUploading: (uploading: boolean) => void; initialSlugTouched?: boolean; }) { - const { localization } = usePluginOverrides< - BlogPluginOverrides, - Partial - >("blog", { - localization: BLOG_LOCALIZATION, - }); + const t = useTranslate(); + const { localization } = usePluginOverrides("blog"); const [slugTouched, setSlugTouched] = useState(initialSlugTouched); + const requiredAsterisk = + localization?.BLOG_FORMS_REQUIRED_ASTERISK ?? + t("blog.forms.requiredAsterisk", " *"); const nameTitle = "title" as FieldPath; const nameSlug = "slug" as FieldPath; const nameExcerpt = "excerpt" as FieldPath; @@ -152,14 +163,16 @@ function PostFormBody({ render={({ field }) => ( - {localization.BLOG_FORMS_TITLE_LABEL} - - {localization.BLOG_FORMS_REQUIRED_ASTERISK} - + {localization?.BLOG_FORMS_TITLE_LABEL ?? + t("blog.forms.titleLabel", "Title")} + {requiredAsterisk} { @@ -188,10 +201,16 @@ function PostFormBody({ return ( - {localization.BLOG_FORMS_SLUG_LABEL} + + {localization?.BLOG_FORMS_SLUG_LABEL ?? + t("blog.forms.slugLabel", "Slug")} + { @@ -217,14 +236,19 @@ function PostFormBody({ render={({ field }) => ( - {localization.BLOG_FORMS_EXCERPT_LABEL} - - {localization.BLOG_FORMS_REQUIRED_ASTERISK} - + {localization?.BLOG_FORMS_EXCERPT_LABEL ?? + t("blog.forms.excerptLabel", "Excerpt")} + {requiredAsterisk}