From 3d9b0514db8cd142da8a0f5293c14a3de13eb805 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 23 Aug 2026 07:37:29 +0000 Subject: [PATCH 1/4] feat(web): bootstrap TanStack Start app Adds `apps/web`, a new TanStack Start (Vite + Nitro) app that will eventually replace the Next.js frontend. Stage 0 only - no VitNode features are ported yet. - Vite config wiring `tanstackStart`, Tailwind 4 and Nitro, with workspace packages bundled rather than externalized for SSR. - TanStack Query set up in the router context and `Wrap` provider up front, so it does not have to be retrofitted later. - Tailwind entry reusing the VitNode design tokens from the Next app, with Geist served through Fontsource instead of `next/font`. - A home page and an `/admin` placeholder, which exist to exercise the two locale strategies landing in the next commit. TanStack packages are pinned to releases older than the repo's `minimumReleaseAge` policy cutoff, so no policy exemptions are needed. `vite` and `@vitejs/plugin-react` are pinned to the versions already in the lockfile to keep every existing resolution unchanged. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WSPLXkitYKMftPVD4L3CVf --- apps/web/.gitignore | 5 + apps/web/eslint.config.mjs | 22 + apps/web/package.json | 35 + apps/web/src/routeTree.gen.ts | 86 +++ apps/web/src/router.tsx | 28 + apps/web/src/routes/__root.tsx | 37 ++ apps/web/src/routes/admin.tsx | 13 + apps/web/src/routes/index.tsx | 13 + apps/web/src/styles/globals.css | 132 ++++ apps/web/tsconfig.json | 17 + apps/web/vite.config.ts | 23 + pnpm-lock.yaml | 1091 +++++++++++++++++++++++++++++-- 12 files changed, 1458 insertions(+), 44 deletions(-) create mode 100644 apps/web/.gitignore create mode 100644 apps/web/eslint.config.mjs create mode 100644 apps/web/package.json create mode 100644 apps/web/src/routeTree.gen.ts create mode 100644 apps/web/src/router.tsx create mode 100644 apps/web/src/routes/__root.tsx create mode 100644 apps/web/src/routes/admin.tsx create mode 100644 apps/web/src/routes/index.tsx create mode 100644 apps/web/src/styles/globals.css create mode 100644 apps/web/tsconfig.json create mode 100644 apps/web/vite.config.ts diff --git a/apps/web/.gitignore b/apps/web/.gitignore new file mode 100644 index 000000000..5cc3fcc7b --- /dev/null +++ b/apps/web/.gitignore @@ -0,0 +1,5 @@ +.output +.nitro +.tanstack +.vite +*.tsbuildinfo diff --git a/apps/web/eslint.config.mjs b/apps/web/eslint.config.mjs new file mode 100644 index 000000000..7c2c6398a --- /dev/null +++ b/apps/web/eslint.config.mjs @@ -0,0 +1,22 @@ +import eslintVitNode from "@vitnode/config/eslint"; +import eslintVitNodeReact from "@vitnode/config/eslint.react"; +import { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default [ + ...eslintVitNode, + ...eslintVitNodeReact, + { + ignores: ["src/routeTree.gen.ts", ".output", ".nitro", ".tanstack"], + }, + { + languageOptions: { + parserOptions: { + project: "./tsconfig.json", + tsconfigRootDir: __dirname, + }, + }, + }, +]; diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 000000000..d54010ea1 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,35 @@ +{ + "name": "web", + "version": "1.2.0-canary.78", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "start": "node .output/server/index.mjs", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@fontsource-variable/geist": "^5.3.0", + "@tanstack/react-query": "^5.101.4", + "@tanstack/react-router": "1.170.31", + "@tanstack/react-start": "1.168.48", + "react": "^19.2.8", + "react-dom": "^19.2.8" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.3.3", + "@types/node": "^26.1.1", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "6.0.4", + "@vitnode/config": "workspace:*", + "eslint": "^10.7.0", + "nitro": "^3.0.260610-beta", + "tailwindcss": "^4.3.3", + "typescript": "^6.0.3", + "vite": "8.1.5" + } +} diff --git a/apps/web/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts new file mode 100644 index 000000000..74f928420 --- /dev/null +++ b/apps/web/src/routeTree.gen.ts @@ -0,0 +1,86 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as IndexRouteImport } from './routes/index' +import { Route as AdminRouteImport } from './routes/admin' + +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const AdminRoute = AdminRouteImport.update({ + id: '/admin', + path: '/admin', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/admin': typeof AdminRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/admin': typeof AdminRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/admin': typeof AdminRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/admin' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/admin' + id: '__root__' | '/' | '/admin' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + AdminRoute: typeof AdminRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/admin': { + id: '/admin' + path: '/admin' + fullPath: '/admin' + preLoaderRoute: typeof AdminRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + AdminRoute: AdminRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/apps/web/src/router.tsx b/apps/web/src/router.tsx new file mode 100644 index 000000000..1131a2993 --- /dev/null +++ b/apps/web/src/router.tsx @@ -0,0 +1,28 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { createRouter } from "@tanstack/react-router"; + +import { routeTree } from "./routeTree.gen"; + +export interface RouterAppContext { + queryClient: QueryClient; +} + +export const getRouter = () => { + const queryClient = new QueryClient(); + + return createRouter({ + routeTree, + context: { queryClient } satisfies RouterAppContext, + scrollRestoration: true, + defaultPreload: "intent", + Wrap: ({ children }) => ( + {children} + ), + }); +}; + +declare module "@tanstack/react-router" { + interface Register { + router: ReturnType; + } +} diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx new file mode 100644 index 000000000..f71ebfb50 --- /dev/null +++ b/apps/web/src/routes/__root.tsx @@ -0,0 +1,37 @@ +import type { QueryClient } from "@tanstack/react-query"; + +import { + createRootRouteWithContext, + HeadContent, + Scripts, +} from "@tanstack/react-router"; + +import appCss from "../styles/globals.css?url"; + +export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()( + { + head: () => ({ + meta: [ + { charSet: "utf-8" }, + { name: "viewport", content: "width=device-width, initial-scale=1" }, + { title: "VitNode" }, + ], + links: [{ rel: "stylesheet", href: appCss }], + }), + shellComponent: RootDocument, + }, +); + +function RootDocument({ children }: { children: React.ReactNode }) { + return ( + + + + + + {children} + + + + ); +} diff --git a/apps/web/src/routes/admin.tsx b/apps/web/src/routes/admin.tsx new file mode 100644 index 000000000..a614b4707 --- /dev/null +++ b/apps/web/src/routes/admin.tsx @@ -0,0 +1,13 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/admin")({ + component: Admin, +}); + +function Admin() { + return ( +
+

Admin

+
+ ); +} diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx new file mode 100644 index 000000000..73049963b --- /dev/null +++ b/apps/web/src/routes/index.tsx @@ -0,0 +1,13 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/")({ + component: Home, +}); + +function Home() { + return ( +
+

VitNode

+
+ ); +} diff --git a/apps/web/src/styles/globals.css b/apps/web/src/styles/globals.css new file mode 100644 index 000000000..1267eec5b --- /dev/null +++ b/apps/web/src/styles/globals.css @@ -0,0 +1,132 @@ +@import "tailwindcss"; +@import "@fontsource-variable/geist"; + +:root:not(.dark) { + --background: oklch(0.97 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.51 0.16 262.61); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --warn: oklch(0.54 0.12 82.58); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.705 0.015 286.067); + --chart-1: oklch(0.871 0.006 286.286); + --chart-2: oklch(0.552 0.016 285.938); + --chart-3: oklch(0.442 0.017 285.786); + --chart-4: oklch(0.37 0.013 285.805); + --chart-5: oklch(0.274 0.006 286.033); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.21 0.006 285.885); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.705 0.015 286.067); +} + +.dark { + --background: oklch(0.14 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.6 0.18 262.65); + --primary-foreground: oklch(0.98 0 0); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --warn: oklch(0.76 0.18 81.84); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.552 0.016 285.938); + --chart-1: oklch(0.871 0.006 286.286); + --chart-2: oklch(0.552 0.016 285.938); + --chart-3: oklch(0.442 0.017 285.786); + --chart-4: oklch(0.37 0.013 285.805); + --chart-5: oklch(0.274 0.006 286.033); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.552 0.016 285.938); +} + +:root { + --radius: 0.625rem; + --font-sans: + "Geist Variable", ui-sans-serif, system-ui, -apple-system, sans-serif; +} + +@theme inline { + --font-heading: var(--font-sans); + --font-sans: var(--font-sans); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-warn: var(--warn); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --color-foreground: var(--foreground); + --color-background: var(--background); + --radius-sm: calc(var(--radius) * 0.6); + --radius-md: calc(var(--radius) * 0.8); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) * 1.4); + --radius-2xl: calc(var(--radius) * 1.8); + --radius-3xl: calc(var(--radius) * 2.2); + --radius-4xl: calc(var(--radius) * 2.6); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json new file mode 100644 index 000000000..88c9f3ef4 --- /dev/null +++ b/apps/web/tsconfig.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vitnode/config/tsconfig", + "compilerOptions": { + "target": "ESNext", + "module": "esnext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "noEmit": true, + "types": ["vite/client"], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts new file mode 100644 index 000000000..b0a4e445b --- /dev/null +++ b/apps/web/vite.config.ts @@ -0,0 +1,23 @@ +import tailwindcss from "@tailwindcss/vite"; +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import viteReact from "@vitejs/plugin-react"; +import { nitro } from "nitro/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + server: { port: 3000 }, + resolve: { tsconfigPaths: true }, + ssr: { + // Workspace packages must be bundled, not externalized. + noExternal: [/^@vitnode\//], + }, + plugins: [ + tailwindcss(), + tanstackStart({ + srcDirectory: "src", + router: { routesDirectory: "routes" }, + }), + viteReact(), + nitro(), + ], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aeb2bd21f..f27d949ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,13 +155,13 @@ importers: version: 1.0.0-rc.4(@opentelemetry/api@1.9.1)(postgres@3.4.9)(zod@4.4.3) fumadocs-core: specifier: ^16.11.5 - version: 16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3) + version: 16.11.5(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3) fumadocs-mdx: specifier: ^15.2.0 - version: 15.2.0(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + version: 15.2.0(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) fumadocs-ui: specifier: ^16.11.5 - version: 16.11.5(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3) + version: 16.11.5(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3) hono: specifier: ^4.12.31 version: 4.12.31 @@ -266,6 +266,61 @@ importers: specifier: ^4.4.3 version: 4.4.3 + apps/web: + dependencies: + '@fontsource-variable/geist': + specifier: ^5.3.0 + version: 5.3.0 + '@tanstack/react-query': + specifier: ^5.101.4 + version: 5.101.4(react@19.2.8) + '@tanstack/react-router': + specifier: 1.170.31 + version: 1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/react-start': + specifier: 1.168.48 + version: 1.168.48(crossws@0.4.12(srvx@0.11.22))(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + react: + specifier: ^19.2.8 + version: 19.2.8 + react-dom: + specifier: ^19.2.8 + version: 19.2.8(react@19.2.8) + devDependencies: + '@tailwindcss/vite': + specifier: ^4.3.3 + version: 4.3.3(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + '@types/node': + specifier: ^26.1.1 + version: 26.1.1 + '@types/react': + specifier: ^19.2.17 + version: 19.2.17 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.17) + '@vitejs/plugin-react': + specifier: 6.0.4 + version: 6.0.4(babel-plugin-react-compiler@1.0.0)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + '@vitnode/config': + specifier: workspace:* + version: link:../../packages/config + eslint: + specifier: ^10.7.0 + version: 10.7.0(jiti@2.7.0) + nitro: + specifier: ^3.0.260610-beta + version: 3.0.260610-beta(chokidar@5.0.0)(dotenv@17.4.2)(jiti@2.7.0)(lru-cache@11.5.2)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + tailwindcss: + specifier: ^4.3.3 + version: 4.3.3 + typescript: + specifier: ^6.0.3 + version: 6.0.3 + vite: + specifier: 8.1.5 + version: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0) + packages/config: dependencies: '@eslint-react/eslint-plugin': @@ -608,7 +663,7 @@ importers: version: 4.12.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-scan: specifier: ^0.5.7 - version: 0.5.7(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(esbuild@0.27.7)(eslint@10.7.0(jiti@2.7.0))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + version: 0.5.7(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(esbuild@0.27.7)(eslint@10.7.0(jiti@2.7.0))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) recharts: specifier: ^3.10.0 version: 3.10.0(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react-is@17.0.2)(react@19.2.8)(redux@5.0.1) @@ -1044,6 +1099,10 @@ packages: resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} engines: {node: '>=18.0.0'} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} @@ -1337,9 +1396,15 @@ packages: '@emnapi/core@1.11.1': resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + '@emnapi/core@1.11.2': + resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} + '@emnapi/runtime@1.11.1': resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@emnapi/runtime@1.11.2': + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} @@ -1923,6 +1988,9 @@ packages: '@floating-ui/utils@0.2.12': resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + '@fontsource-variable/geist@5.3.0': + resolution: {integrity: sha512-j0m+vLQuG5XAYoHtGCVu0spvlGreR3EzpECUVzkFmI1mTVnAO38l/NEPDCFgZ177JxzYJCLSmTQibIiYPilGrA==} + '@formatjs/fast-memoize@3.1.7': resolution: {integrity: sha512-zXfhLpvA6T7+efdt9JLbBwZ00tT7NsBMDVnDu8rpHeNNv8KfRZAMo2gkG0k9lK/Nzc//3kJ9pImsfuJxk3KhUA==} @@ -2736,6 +2804,22 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@oozcitak/dom@2.0.2': + resolution: {integrity: sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==} + engines: {node: '>=20.0'} + + '@oozcitak/infra@2.0.2': + resolution: {integrity: sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==} + engines: {node: '>=20.0'} + + '@oozcitak/url@3.0.0': + resolution: {integrity: sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==} + engines: {node: '>=20.0'} + + '@oozcitak/util@10.0.0': + resolution: {integrity: sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==} + engines: {node: '>=20.0'} + '@opentelemetry/api-logs@0.220.0': resolution: {integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==} engines: {node: '>=8.0.0'} @@ -4053,18 +4137,36 @@ packages: cpu: [arm64] os: [darwin] + '@swc/core-darwin-arm64@1.15.47': + resolution: {integrity: sha512-GsoMtan3ojGGMGFbl31mmRu5ctZ56re8grGE8mO/OHJ8O+JRkzod02fe7X6ZQ8JvamA3imkEkx/h3u+vsOgPgA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + '@swc/core-darwin-x64@1.15.46': resolution: {integrity: sha512-4Tj4ppVIPCmUMpmGFiGtyEriwLyJ+yi/US4WfBrP/ok8COGddDZXLEzQETnKyK46mjvr1v0jevrS23zjoff7vA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] + '@swc/core-darwin-x64@1.15.47': + resolution: {integrity: sha512-leTi7Rx3KF4zcC637iqWgk9SoV8VXAD8ppQYXsep63px5A/UftOcxLN1pmr8Z1si/YvX90ompP/rHgpYkgwXWg==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + '@swc/core-linux-arm-gnueabihf@1.15.46': resolution: {integrity: sha512-i8tUGnNjyOgMmfmgFSg4aeJLQoFyfpIHK5FjpQAwpRyQIqEUB2w1e8zIDQzY1WhOxx8NoS1S5iUL813Un4Sf5A==} engines: {node: '>=10'} cpu: [arm] os: [linux] + '@swc/core-linux-arm-gnueabihf@1.15.47': + resolution: {integrity: sha512-hBqHuoWKKIsKmDBn9qVeWqj5GWZhtlcczVaqQmNRXsDfq+voR5CxKRfamA367QjJXtceYuliLFfEL8QsskRM2g==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + '@swc/core-linux-arm64-gnu@1.15.46': resolution: {integrity: sha512-c0OnhqzdhfOvv6qhNCcByepB+sNYOGZyhtr2Qa6ZCHvAWTYhSRw4j/u92Stue9PbZ/6q74b9nHzi76+kVzqQHQ==} engines: {node: '>=10'} @@ -4072,6 +4174,13 @@ packages: os: [linux] libc: [glibc] + '@swc/core-linux-arm64-gnu@1.15.47': + resolution: {integrity: sha512-TBxvRz+B4K205TWHHZxWVxkC2RFNP/Mz3PNcECBos5PsKwxjg3QSJzdoebr0VCf0Bfh8HOPldKxAP/8XkFe9gA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@swc/core-linux-arm64-musl@1.15.46': resolution: {integrity: sha512-imyRpNEcUzFQFV2LE4jL68ErvmKEuZCbvZru77iQREunJ+bR4i658cupTgtG1mLYM3F1Tzy3Sb9xYb02KghWTg==} engines: {node: '>=10'} @@ -4079,6 +4188,13 @@ packages: os: [linux] libc: [musl] + '@swc/core-linux-arm64-musl@1.15.47': + resolution: {integrity: sha512-3Yu3Uq/VgytqsPjTMbkPU1ExADytbdWbruJYhA584E9jrpE2Ki+R6VVPoZCeAVk1Cb7QxcRTgblw6bSa6a/R+w==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [musl] + '@swc/core-linux-ppc64-gnu@1.15.46': resolution: {integrity: sha512-ctEfcl/HcUeomK33cbySiHZm98GEDIxTm1EkpBsYCiHxElYBzvTXVeuQT2YwbUXn9XCrjiw4ipyUNk33k26qRg==} engines: {node: '>=10'} @@ -4086,6 +4202,13 @@ packages: os: [linux] libc: [glibc] + '@swc/core-linux-ppc64-gnu@1.15.47': + resolution: {integrity: sha512-wfdMi5IaOaNtmh2/6geRoxIdNfqylUZFdtzTKS655y1axWfIWyx7As74vv0wVdjeCIZ3WmCI9odDd4rUttXOSQ==} + engines: {node: '>=10'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@swc/core-linux-s390x-gnu@1.15.46': resolution: {integrity: sha512-DxlMdnt84TtRVTv7WL/thWyz9+QU8QZNNoAP9rrk0P68LziuhfePp8MjQ44zIprpTHTsEwyziIuGUUN5iSC1bQ==} engines: {node: '>=10'} @@ -4093,6 +4216,13 @@ packages: os: [linux] libc: [glibc] + '@swc/core-linux-s390x-gnu@1.15.47': + resolution: {integrity: sha512-3hHYBY0yx8Ez7GMRrkhXHQzMdR5IZA6Wq5Ee4svlgwvSECLpnAJ9+0AimEGUFDvuLwE7nV/2+PYe8+Nm4rvNcQ==} + engines: {node: '>=10'} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@swc/core-linux-x64-gnu@1.15.46': resolution: {integrity: sha512-SKxI7J6t90XPl8hRUqtJi9NfGdunN/E/vZMc7Bc0figeRdOPDBT+Tm8g7cx9xM0T0mewh2l+8dewa3Am27/P+A==} engines: {node: '>=10'} @@ -4100,6 +4230,13 @@ packages: os: [linux] libc: [glibc] + '@swc/core-linux-x64-gnu@1.15.47': + resolution: {integrity: sha512-TjfhjgP/jGCfFHYC3JQPhJA1HwErbIJ9JfREDc1KNkvY6P0LodCgKVIlQ5deeTbkG7ih3bF5PHJLuLpaZjdRyQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [glibc] + '@swc/core-linux-x64-musl@1.15.46': resolution: {integrity: sha512-qj9T6B7bosI0VEsrWOVXZN1OXxS8Tp63ywyrLxNdOycnUtLdkgYcoBsN5y8ImnDDsnwrEWZOy1e+J4xSe7mA3Q==} engines: {node: '>=10'} @@ -4107,24 +4244,49 @@ packages: os: [linux] libc: [musl] + '@swc/core-linux-x64-musl@1.15.47': + resolution: {integrity: sha512-CQpS8Ge/avfjZd0UEwG/sds83Uu32deQXcV1Jo3jD0mmvQQqtYAjpsDZXugmheeAwmt+YIuoVtVHro8LMYHqsQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [musl] + '@swc/core-win32-arm64-msvc@1.15.46': resolution: {integrity: sha512-8p7l4c3LU+eA5g9Et1JPhNeMC1oQwXTGU+uah8DPIBX7YXzqswvaBtyKVmXefVGi/DJU1x3YJsc3mbAp9aWzSQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] + '@swc/core-win32-arm64-msvc@1.15.47': + resolution: {integrity: sha512-0W8IKHsUTYiT7G2RqtOoVWk+89yzZikIiDUb/sCK6BmQDBhN91hQSfyUtW12jhEWLzYgcfmisfsZrmZE+84U1A==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + '@swc/core-win32-ia32-msvc@1.15.46': resolution: {integrity: sha512-tUEnfr3Bn9u6FOjUb3PN9p+09qZC2j+wNDLKHzXXZn22rqGcUqR/ohCRSS+nG9B9+X+U+3FewNEHJkTmdIvMjQ==} engines: {node: '>=10'} cpu: [ia32] os: [win32] + '@swc/core-win32-ia32-msvc@1.15.47': + resolution: {integrity: sha512-ZIp49d2Z4/ka2jO9otOg4hDvTdPmp86kVOgS2M5FCPI7eKKZ1W0boxWn+8XeZrfERtFGW0AlMRm4JhlJa7l3NA==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + '@swc/core-win32-x64-msvc@1.15.46': resolution: {integrity: sha512-Vux7UDzBJYQggSuPfcl2w9iu+IJpgpRCxHzgCaVkELnAXAE4XZMOTX9HNcaNiwfeIDqdu2rkr69RuDm6wY8neA==} engines: {node: '>=10'} cpu: [x64] os: [win32] + '@swc/core-win32-x64-msvc@1.15.47': + resolution: {integrity: sha512-2h8Iek95vnixkBRCo+H8p09+Q5ll2NgSMFrWTy0iKt7+/t+8/T5mBpiT6c0ZxSS7wcWjwZ9sGZkK70tTSYHdDw==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + '@swc/core@1.15.46': resolution: {integrity: sha512-Ri3em2mBpq3h2zSPliCYl63otDGqek8PPEfv2nWgRQEbZ/VBCNyypVTVQ6cEbTCXBhy+WE2T3fQb08moIyuYaw==} engines: {node: '>=10'} @@ -4247,6 +4409,15 @@ packages: '@tailwindcss/postcss@4.3.3': resolution: {integrity: sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==} + '@tailwindcss/vite@4.3.3': + resolution: {integrity: sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@tanstack/history@1.162.1': + resolution: {integrity: sha512-DR9t6lfLVdrjgCwpglrR9DR7Ok8/HlXjcOE+goWXF3zyuLUO/ug7vMbSFxTqrQTtbRghJfyhmIZ0S6LhPIy44w==} + engines: {node: '>=20.19'} + '@tanstack/query-core@5.101.4': resolution: {integrity: sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==} @@ -4255,6 +4426,135 @@ packages: peerDependencies: react: ^18 || ^19 + '@tanstack/react-router@1.170.31': + resolution: {integrity: sha512-jqITLcf9Y+es9Wm7fD7XfXFGKk1Fujm+okGqHtr+UhISnQuyLQ8d3frsJlIN0Np1doYoeb5weozSkv72+EZ5bw==} + engines: {node: '>=20.19'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-client@1.168.29': + resolution: {integrity: sha512-YhEtx3EwbsLUWXSdwclz/C/AlDqPNoNMoNTEHulYDruaMAKNbFeWsDgBbHv/aE6P2P3shAMFYAT1cVHZM1WcJQ==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-rsc@0.1.47': + resolution: {integrity: sha512-Tr01FbuRRpHSwkLjyfg7OotXgrJ4ufDjGKuk9HpLe7a2ouEs1Iq/kC2RKY0alvCYX+cpYFVayoyxNZCeybCTNQ==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rspack/core': '>=2.0.0-0' + '@vitejs/plugin-rsc': '>=0.5.30' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + react-server-dom-rspack: '>=0.0.2' + peerDependenciesMeta: + '@rspack/core': + optional: true + '@vitejs/plugin-rsc': + optional: true + react-server-dom-rspack: + optional: true + + '@tanstack/react-start-server@1.167.36': + resolution: {integrity: sha512-v22xUhNENr0u67AIDbfbJgmQwMGqcrwdqqNI44Nd605G4DGTA7W1KPbea8bBjPkHxf5I+FTKL8Nb56w2Sb/Gpw==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start@1.168.48': + resolution: {integrity: sha512-ANBtq/phgjPzfwgnB40kyREQGhrLONueJg9y+oFuGIuUVaTfqWjX5y820jsJ0FiZ94JU+WIV6GebVhphNhuYjg==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rsbuild/core': ^2.0.0 + '@vitejs/plugin-rsc': '*' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + vite: '>=7.0.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@vitejs/plugin-rsc': + optional: true + vite: + optional: true + + '@tanstack/react-store@0.9.3': + resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-core@1.171.26': + resolution: {integrity: sha512-VymmPSs/93szHur/7PBygT6tRbmfcNO1xR46Wn/JVHeBqVduLPCuxBeJEgfVmVq8E4hSklPEh8i6qGXQd6WRqA==} + engines: {node: '>=20.19'} + + '@tanstack/router-generator@1.167.32': + resolution: {integrity: sha512-KKPWUMUda7JYil+uDQPrX4ecyoXP9J3DesdpavOWpGCavsiAd6cxH6yq18krLGa1j20qmPJc10LJSLOOFia5tQ==} + engines: {node: '>=20.19'} + + '@tanstack/router-plugin@1.168.34': + resolution: {integrity: sha512-WsXLQU8tQmoTyoQRB1JI3lGg5wnwo6VB2R8D2RQbtCVlUi3bjreSNgZSJ7Ghw06ZoUyNMO+Csiqf679nNnzN+w==} + engines: {node: '>=20.19'} + peerDependencies: + '@rsbuild/core': '>=1.0.2 || ^2.0.0' + '@tanstack/react-router': ^1.170.31 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0' + vite-plugin-solid: ^2.11.10 || ^3.0.0-0 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + + '@tanstack/router-utils@1.162.2': + resolution: {integrity: sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ==} + engines: {node: '>=20.19'} + + '@tanstack/start-client-core@1.170.26': + resolution: {integrity: sha512-a8SmEQ4gIeWDHBSqy1ePFNwXu0kHTPCoMiEdE0eKRBITVsexfEX8yKvqxLi66cF6aGCmDIkndbth3yB7R344zQ==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-fn-stubs@1.162.0': + resolution: {integrity: sha512-QWfUZ3Yo923tdQn38LyKMU8rcTw69zc+T4dAvgTWV4O56SqFRsGfS0lSWIMhJRwXIx/bvdi7nTUBDdZtTHtpTQ==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-plugin-core@1.171.38': + resolution: {integrity: sha512-ldRjxosoTyWG0xP6CLo/DkiuENt668se7oZwAJuRMtRBGBDzhMVMwFnS86P2qgnpj/BQVFebVpXMVfzeH7tvDw==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rsbuild/core': ^2.0.0 + vite: '>=7.0.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + vite: + optional: true + + '@tanstack/start-server-core@1.169.30': + resolution: {integrity: sha512-knlKOPsHyDbGKgP1pfUowtObZIKS9zRI6vl7dzbA8D/NJo6dMdDdqGcDf4PINqREW8hCHdkB4At0y1/Mk+L2SQ==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-storage-context@1.167.28': + resolution: {integrity: sha512-ZGl6bEXW5m77VVtWMt/naEBVoIeQkXYgF4UgiJM/3z3e1wbRzaoW7Dnc4HW+hoBPOOdfiVVNx31RT2ARnXWAiQ==} + engines: {node: '>=22.12.0'} + + '@tanstack/store@0.9.3': + resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==} + + '@tanstack/virtual-file-routes@1.162.0': + resolution: {integrity: sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA==} + engines: {node: '>=20.19'} + '@tiptap/core@3.28.0': resolution: {integrity: sha512-gUuD5WAYfbDxNSSJya/emh2KSzXZXLUYKW4fEnc1AQ5FE2twzh4LJ9UlKFIawigrUCAksWI5Fy1hRbv+5m4ZdQ==} peerDependencies: @@ -4857,6 +5157,10 @@ packages: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} + engines: {node: '>=14'} + any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -4952,6 +5256,9 @@ packages: react-native-b4a: optional: true + babel-dead-code-elimination@1.0.12: + resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} + babel-plugin-react-compiler@1.0.0: resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==} @@ -5270,6 +5577,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -5303,6 +5613,14 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + crossws@0.4.12: + resolution: {integrity: sha512-aypfsr6t0uNvkqaZc6zvBfXzC6pLI0/sIulpkV6RwCVtZqG5ebBzv4weImKK0VNCj91Wl9F5j7p5WU4MNrybng==} + peerDependencies: + srvx: '>=0.11.5' + peerDependenciesMeta: + srvx: + optional: true + css-in-js-utils@3.1.0: resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} @@ -5388,6 +5706,29 @@ packages: date-fns@4.4.0: resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} + 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 + debounce-fn@4.0.0: resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==} engines: {node: '>=10'} @@ -5740,6 +6081,24 @@ packages: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + 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==} @@ -6024,6 +6383,9 @@ packages: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} + exsolve@1.1.1: + resolution: {integrity: sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==} + ext-list@2.2.2: resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==} engines: {node: '>=0.10.0'} @@ -6091,6 +6453,9 @@ packages: picomatch: optional: true + fetchdts@0.1.7: + resolution: {integrity: sha512-YoZjBdafyLIop9lSxXVI33oLD5kN31q4Td+CasofLLYeLXRFeOsuOw0Uo+XNRi9PZlbfdlN2GmRtm4tCEQ9/KA==} + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -6410,6 +6775,26 @@ packages: resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} engines: {node: '>=10'} + h3@2.0.1-rc.20: + resolution: {integrity: sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg==} + engines: {node: '>=20.11.1'} + hasBin: true + peerDependencies: + crossws: ^0.4.1 + peerDependenciesMeta: + 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 + has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -6474,6 +6859,9 @@ packages: resolution: {integrity: sha512-zJIHFrl6bq3RDd2YusFNCDlM8qUprxKswyi/OPzPyzKDdyBXDqWx8bZlZ7R+saTdSTatUmb3O7K4SspGPaEOQg==} engines: {node: '>=16.9.0'} + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + hpagent@1.2.0: resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==} engines: {node: '>=14'} @@ -6509,6 +6897,9 @@ packages: resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} engines: {node: '>=10.19.0'} + httpxy@0.5.5: + resolution: {integrity: sha512-uDjmnPyp1q4Sgzf3w+J/Fc6UqcCEj0x4Wjp7OqK5dGhNeDgpyrAmnS6ey8QWrX3SWDon2DMKf9sBa5X9+CVyMA==} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -6801,6 +7192,10 @@ packages: isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isbot@5.2.1: + resolution: {integrity: sha512-dJ+LpKyClQZ7NG+j3OensC/mAZkGpukE9YUrgPYvAZj2doVL0edfDgywTUh5CXa0o+nW9a1V9e5+CJTX8+SxRw==} + engines: {node: '>=18'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -7541,6 +7936,40 @@ packages: sass: optional: true + nf3@0.3.24: + resolution: {integrity: sha512-HxLK4bo+5jNsEETZp4w3tJblHOA9MCBY14IN9nZJJV8JDxt9yNIYxuBuLMjTAR5GFa3HL61+8VQDUrXv3/C8fw==} + + nitro@3.0.260610-beta: + resolution: {integrity: sha512-KPb4L5yaF/Rx/xoGMpgHRJvZhbhGiqbRKOwwPLCH9jKTKTsEUHLjnJas85AeCzaswqa8Wi52eQBtRsODC4PS0Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@vercel/queue': ^0.3.0 + dotenv: '*' + giget: '*' + jiti: ^2.7.0 + rollup: ^4.61.1 + 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 + node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} @@ -7613,6 +8042,15 @@ packages: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} 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.12: + resolution: {integrity: sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -8341,6 +8779,9 @@ packages: rope-sequence@1.3.4: resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + rou3@0.8.1: + resolution: {integrity: sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA==} + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -8418,6 +8859,16 @@ packages: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} + seroval-plugins@1.6.2: + resolution: {integrity: sha512-TfxuUjlbBESzUOWdTkTKqvSmav0ABym+itetDXLK6mDz8SmrpdI30aF8RTXE8Bvq+tH/1yIDkvy3W0lfQb1ipQ==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.6.2: + resolution: {integrity: sha512-mPT+SD2TrlB6wvte1KkYOYUkubaTbd6pZ/6Kk3C9nxzrHmCZyhxOO7XGAeL7f+yLKZglzGtM9odUVvg/EhO+vQ==} + engines: {node: '>=10'} + serve-static@2.2.1: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} @@ -8555,6 +9006,11 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + srvx@0.11.22: + resolution: {integrity: sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ==} + engines: {node: '>=20.16.0'} + hasBin: true + stack-generator@2.0.10: resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} @@ -8955,6 +9411,9 @@ packages: resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} 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'} @@ -9024,16 +9483,90 @@ packages: webpack: optional: true - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} - hasBin: true + unstorage@2.0.0-alpha.7: + resolution: {integrity: sha512-ELPztchk2zgFJnakyodVY3vJWGW9jy//keJ32IOJVGUMyaPydwcA1FtVvWqT0TNRch9H+cMNEGllfVFfScImog==} peerDependencies: - browserslist: '>= 4.21.0' - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - use-callback-ref@1.3.3: + '@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 + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-callback-ref@1.3.3: resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'} peerDependencies: @@ -9145,6 +9678,14 @@ packages: yaml: optional: true + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + vitest@4.1.10: resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -9312,6 +9853,10 @@ packages: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} + xmlbuilder2@4.0.3: + resolution: {integrity: sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==} + engines: {node: '>=20.0'} + xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} @@ -9619,6 +10164,12 @@ snapshots: '@aws/lambda-invoke-store@0.3.0': {} + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 @@ -9983,11 +10534,22 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.2': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.11.1': dependencies: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.2': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.2.2': dependencies: tslib: 2.8.1 @@ -10366,6 +10928,8 @@ snapshots: '@floating-ui/utils@0.2.12': {} + '@fontsource-variable/geist@5.3.0': {} + '@formatjs/fast-memoize@3.1.7': {} '@formatjs/icu-messageformat-parser@3.5.15': @@ -10912,6 +11476,13 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@tybys/wasm-util': 0.10.3 + optional: true + '@next/bundle-analyzer@16.3.1': dependencies: webpack-bundle-analyzer: 4.10.1 @@ -10983,6 +11554,23 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@oozcitak/dom@2.0.2': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/url': 3.0.0 + '@oozcitak/util': 10.0.0 + + '@oozcitak/infra@2.0.2': + dependencies: + '@oozcitak/util': 10.0.0 + + '@oozcitak/url@3.0.0': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + + '@oozcitak/util@10.0.0': {} + '@opentelemetry/api-logs@0.220.0': dependencies: '@opentelemetry/api': 1.9.1 @@ -11137,12 +11725,11 @@ snapshots: '@oxc-resolver/binding-openharmony-arm64@11.24.2': optional: true - '@oxc-resolver/binding-wasm32-wasi@11.24.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@oxc-resolver/binding-wasm32-wasi@11.24.2': dependencies: - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': @@ -12006,39 +12593,75 @@ snapshots: '@swc/core-darwin-arm64@1.15.46': optional: true + '@swc/core-darwin-arm64@1.15.47': + optional: true + '@swc/core-darwin-x64@1.15.46': optional: true + '@swc/core-darwin-x64@1.15.47': + optional: true + '@swc/core-linux-arm-gnueabihf@1.15.46': optional: true + '@swc/core-linux-arm-gnueabihf@1.15.47': + optional: true + '@swc/core-linux-arm64-gnu@1.15.46': optional: true + '@swc/core-linux-arm64-gnu@1.15.47': + optional: true + '@swc/core-linux-arm64-musl@1.15.46': optional: true + '@swc/core-linux-arm64-musl@1.15.47': + optional: true + '@swc/core-linux-ppc64-gnu@1.15.46': optional: true + '@swc/core-linux-ppc64-gnu@1.15.47': + optional: true + '@swc/core-linux-s390x-gnu@1.15.46': optional: true + '@swc/core-linux-s390x-gnu@1.15.47': + optional: true + '@swc/core-linux-x64-gnu@1.15.46': optional: true + '@swc/core-linux-x64-gnu@1.15.47': + optional: true + '@swc/core-linux-x64-musl@1.15.46': optional: true + '@swc/core-linux-x64-musl@1.15.47': + optional: true + '@swc/core-win32-arm64-msvc@1.15.46': optional: true + '@swc/core-win32-arm64-msvc@1.15.47': + optional: true + '@swc/core-win32-ia32-msvc@1.15.46': optional: true + '@swc/core-win32-ia32-msvc@1.15.47': + optional: true + '@swc/core-win32-x64-msvc@1.15.46': optional: true + '@swc/core-win32-x64-msvc@1.15.47': + optional: true + '@swc/core@1.15.46(@swc/helpers@0.5.23)': dependencies: '@swc/counter': 0.1.3 @@ -12063,6 +12686,18 @@ snapshots: '@swc/counter': 0.1.3 '@swc/types': 0.1.27 optionalDependencies: + '@swc/core-darwin-arm64': 1.15.47 + '@swc/core-darwin-x64': 1.15.47 + '@swc/core-linux-arm-gnueabihf': 1.15.47 + '@swc/core-linux-arm64-gnu': 1.15.47 + '@swc/core-linux-arm64-musl': 1.15.47 + '@swc/core-linux-ppc64-gnu': 1.15.47 + '@swc/core-linux-s390x-gnu': 1.15.47 + '@swc/core-linux-x64-gnu': 1.15.47 + '@swc/core-linux-x64-musl': 1.15.47 + '@swc/core-win32-arm64-msvc': 1.15.47 + '@swc/core-win32-ia32-msvc': 1.15.47 + '@swc/core-win32-x64-msvc': 1.15.47 '@swc/helpers': 0.5.23 '@swc/counter@0.1.3': {} @@ -12148,6 +12783,15 @@ snapshots: postcss: 8.5.22 tailwindcss: 4.3.3 + '@tailwindcss/vite@4.3.3(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0))': + dependencies: + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + tailwindcss: 4.3.3 + vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0) + + '@tanstack/history@1.162.1': {} + '@tanstack/query-core@5.101.4': {} '@tanstack/react-query@5.101.4(react@19.2.8)': @@ -12155,6 +12799,219 @@ snapshots: '@tanstack/query-core': 5.101.4 react: 19.2.8 + '@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/history': 1.162.1 + '@tanstack/react-store': 0.9.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-core': 1.171.26 + isbot: 5.2.1 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + '@tanstack/react-start-client@1.168.29(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/react-router': 1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-core': 1.171.26 + '@tanstack/start-client-core': 1.170.26 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + '@tanstack/react-start-rsc@0.1.47(crossws@0.4.12(srvx@0.11.22))(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0))': + dependencies: + '@tanstack/react-router': 1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-core': 1.171.26 + '@tanstack/router-utils': 1.162.2 + '@tanstack/start-client-core': 1.170.26 + '@tanstack/start-fn-stubs': 1.162.0 + '@tanstack/start-plugin-core': 1.171.38(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(crossws@0.4.12(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + '@tanstack/start-storage-context': 1.167.28 + pathe: 2.0.3 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + transitivePeerDependencies: + - '@farmfe/core' + - '@rsbuild/core' + - bun-types-no-globals + - crossws + - esbuild + - rolldown + - rollup + - supports-color + - unloader + - vite + - vite-plugin-solid + - webpack + + '@tanstack/react-start-server@1.167.36(crossws@0.4.12(srvx@0.11.22))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/react-router': 1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-core': 1.171.26 + '@tanstack/start-server-core': 1.169.30(crossws@0.4.12(srvx@0.11.22)) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + transitivePeerDependencies: + - crossws + + '@tanstack/react-start@1.168.48(crossws@0.4.12(srvx@0.11.22))(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0))': + dependencies: + '@tanstack/react-router': 1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/react-start-client': 1.168.29(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/react-start-rsc': 0.1.47(crossws@0.4.12(srvx@0.11.22))(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + '@tanstack/react-start-server': 1.167.36(crossws@0.4.12(srvx@0.11.22))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-utils': 1.162.2 + '@tanstack/start-client-core': 1.170.26 + '@tanstack/start-plugin-core': 1.171.38(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(crossws@0.4.12(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + '@tanstack/start-server-core': 1.169.30(crossws@0.4.12(srvx@0.11.22)) + pathe: 2.0.3 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + optionalDependencies: + vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - crossws + - esbuild + - react-server-dom-rspack + - rolldown + - rollup + - supports-color + - unloader + - vite-plugin-solid + - webpack + + '@tanstack/react-store@0.9.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/store': 0.9.3 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + use-sync-external-store: 1.6.0(react@19.2.8) + + '@tanstack/router-core@1.171.26': + dependencies: + '@tanstack/history': 1.162.1 + cookie-es: 3.1.1 + seroval: 1.6.2 + seroval-plugins: 1.6.2(seroval@1.6.2) + + '@tanstack/router-generator@1.167.32': + dependencies: + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.26 + '@tanstack/router-utils': 1.162.2 + '@tanstack/virtual-file-routes': 1.162.0 + jiti: 2.7.0 + magic-string: 0.30.21 + prettier: 3.9.6 + zod: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.168.34(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0))': + dependencies: + '@babel/core': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.26 + '@tanstack/router-generator': 1.167.32 + '@tanstack/router-utils': 1.162.2 + chokidar: 5.0.0 + unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + zod: 4.4.3 + optionalDependencies: + '@tanstack/react-router': 1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - supports-color + - unloader + + '@tanstack/router-utils@1.162.2': + dependencies: + '@babel/generator': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + ansis: 4.3.1 + babel-dead-code-elimination: 1.0.12 + diff: 8.0.4 + pathe: 2.0.3 + tinyglobby: 0.2.17 + transitivePeerDependencies: + - supports-color + + '@tanstack/start-client-core@1.170.26': + dependencies: + '@tanstack/router-core': 1.171.26 + '@tanstack/start-fn-stubs': 1.162.0 + '@tanstack/start-storage-context': 1.167.28 + seroval: 1.6.2 + + '@tanstack/start-fn-stubs@1.162.0': {} + + '@tanstack/start-plugin-core@1.171.38(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(crossws@0.4.12(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0))': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/core': 7.29.7 + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.26 + '@tanstack/router-generator': 1.167.32 + '@tanstack/router-plugin': 1.168.34(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + '@tanstack/router-utils': 1.162.2 + '@tanstack/start-server-core': 1.169.30(crossws@0.4.12(srvx@0.11.22)) + exsolve: 1.1.1 + lightningcss: 1.33.0 + pathe: 2.0.3 + picomatch: 4.0.5 + seroval: 1.6.2 + source-map: 0.7.6 + srvx: 0.11.22 + tinyglobby: 0.2.17 + ufo: 1.6.4 + vitefu: 1.1.3(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + xmlbuilder2: 4.0.3 + zod: 4.4.3 + optionalDependencies: + vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - '@tanstack/react-router' + - bun-types-no-globals + - crossws + - esbuild + - rolldown + - rollup + - supports-color + - unloader + - vite-plugin-solid + - webpack + + '@tanstack/start-server-core@1.169.30(crossws@0.4.12(srvx@0.11.22))': + dependencies: + '@tanstack/history': 1.162.1 + '@tanstack/router-core': 1.171.26 + '@tanstack/start-client-core': 1.170.26 + '@tanstack/start-storage-context': 1.167.28 + fetchdts: 0.1.7 + h3-v2: h3@2.0.1-rc.20(crossws@0.4.12(srvx@0.11.22)) + seroval: 1.6.2 + transitivePeerDependencies: + - crossws + + '@tanstack/start-storage-context@1.167.28': + dependencies: + '@tanstack/router-core': 1.171.26 + + '@tanstack/store@0.9.3': {} + + '@tanstack/virtual-file-routes@1.162.0': {} + '@tiptap/core@3.28.0(@tiptap/pm@3.28.0)': dependencies: '@tiptap/pm': 3.28.0 @@ -12852,6 +13709,8 @@ snapshots: ansi-regex@6.2.2: {} + ansis@4.3.1: {} + any-promise@1.3.0: {} anymatch@3.1.3: @@ -12952,6 +13811,15 @@ snapshots: b4a@1.8.1: {} + 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 @@ -13238,6 +14106,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie-es@3.1.1: {} + cookie-signature@1.2.2: {} cookie@0.7.2: {} @@ -13270,6 +14140,10 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crossws@0.4.12(srvx@0.11.22): + optionalDependencies: + srvx: 0.11.22 + css-in-js-utils@3.1.0: dependencies: hyphenate-style-name: 1.1.0 @@ -13355,6 +14229,8 @@ snapshots: date-fns@4.4.0: {} + db0@0.3.4: {} + debounce-fn@4.0.0: dependencies: mimic-fn: 3.1.0 @@ -13416,17 +14292,14 @@ snapshots: dequal@2.0.3: {} - deslop-js@0.9.12(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1): + deslop-js@0.9.12: dependencies: '@oxc-project/types': 0.143.0 fast-glob: 3.3.3 minimatch: 10.2.5 oxc-parser: 0.143.0 - oxc-resolver: 11.24.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + oxc-resolver: 11.24.2 typescript: 5.9.3 - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' detect-libc@2.1.2: {} @@ -13553,6 +14426,13 @@ snapshots: env-paths@3.0.0: {} + env-runner@0.1.16: + dependencies: + crossws: 0.4.12(srvx@0.11.22) + exsolve: 1.1.1 + httpxy: 0.5.5 + srvx: 0.11.22 + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -14122,6 +15002,8 @@ snapshots: transitivePeerDependencies: - supports-color + exsolve@1.1.1: {} + ext-list@2.2.2: dependencies: mime-db: 1.54.0 @@ -14179,6 +15061,8 @@ snapshots: optionalDependencies: picomatch: 4.0.5 + fetchdts@0.1.7: {} + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -14278,7 +15162,7 @@ snapshots: fsevents@2.3.3: optional: true - fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3): + fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3): dependencies: '@orama/orama': 3.1.18 estree-util-value-to-estree: 3.5.0 @@ -14299,6 +15183,7 @@ snapshots: yaml: 2.9.0 optionalDependencies: '@mdx-js/mdx': 3.1.1 + '@tanstack/react-router': 1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.5 '@types/mdast': 4.0.4 @@ -14311,14 +15196,14 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-mdx@15.2.0(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)): + fumadocs-mdx@15.2.0(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)): dependencies: '@mdx-js/mdx': 3.1.1 '@standard-schema/spec': 1.1.0 chokidar: 5.0.0 esbuild: 0.28.1 estree-util-value-to-estree: 3.5.0 - fumadocs-core: 16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3) + fumadocs-core: 16.11.5(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3) github-slugger: 2.0.0 magic-string: 0.30.21 mdast-util-mdx: 3.0.0 @@ -14344,7 +15229,7 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-ui@16.11.5(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3): + fumadocs-ui@16.11.5(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3): dependencies: '@fuma-translate/react': 1.0.2(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@fumadocs/tailwind': 0.1.1(tailwindcss@4.3.3) @@ -14360,7 +15245,7 @@ snapshots: '@radix-ui/react-tabs': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) class-variance-authority: 0.7.1 cnfast: 0.0.8 - fumadocs-core: 16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3) + fumadocs-core: 16.11.5(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3) lucide-react: 1.25.0(react@19.2.8) motion: 12.42.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) next-themes: 0.4.6(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -14502,6 +15387,20 @@ snapshots: dependencies: duplexer: 0.1.2 + h3@2.0.1-rc.20(crossws@0.4.12(srvx@0.11.22)): + dependencies: + rou3: 0.8.1 + srvx: 0.11.22 + optionalDependencies: + crossws: 0.4.12(srvx@0.11.22) + + h3@2.0.1-rc.22(crossws@0.4.12(srvx@0.11.22)): + dependencies: + rou3: 0.8.1 + srvx: 0.11.22 + optionalDependencies: + crossws: 0.4.12(srvx@0.11.22) + has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -14640,6 +15539,8 @@ snapshots: hono@4.12.31: {} + hookable@6.1.1: {} + hpagent@1.2.0: {} html-encoding-sniffer@6.0.0: @@ -14684,6 +15585,8 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 + httpxy@0.5.5: {} + human-signals@2.1.0: {} human-signals@5.0.0: {} @@ -14930,6 +15833,8 @@ snapshots: isarray@2.0.5: {} + isbot@5.2.1: {} + isexe@2.0.0: {} isexe@3.1.5: {} @@ -15852,6 +16757,61 @@ snapshots: - '@types/node' - babel-plugin-macros + nf3@0.3.24: {} + + nitro@3.0.260610-beta(chokidar@5.0.0)(dotenv@17.4.2)(jiti@2.7.0)(lru-cache@11.5.2)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)): + dependencies: + consola: 3.4.2 + crossws: 0.4.12(srvx@0.11.22) + db0: 0.3.4 + env-runner: 0.1.16 + h3: 2.0.1-rc.22(crossws@0.4.12(srvx@0.11.22)) + hookable: 6.1.1 + nf3: 0.3.24 + ocache: 0.1.5 + ofetch: 2.0.0-alpha.3 + ohash: 2.0.12 + rolldown: 1.1.5 + srvx: 0.11.22 + unenv: 2.0.0-rc.24 + unstorage: 2.0.0-alpha.7(chokidar@5.0.0)(db0@0.3.4)(lru-cache@11.5.2)(ofetch@2.0.0-alpha.3) + optionalDependencies: + dotenv: 17.4.2 + jiti: 2.7.0 + rollup: 4.62.2 + vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0) + 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 + node-addon-api@7.1.1: {} node-cron@4.6.0: {} @@ -15916,6 +16876,14 @@ snapshots: obug@2.1.4: {} + ocache@0.1.5: + dependencies: + ohash: 2.0.12 + + ofetch@2.0.0-alpha.3: {} + + ohash@2.0.12: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -16030,7 +16998,7 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc': 0.143.0 '@oxc-parser/binding-win32-x64-msvc': 0.143.0 - oxc-resolver@11.24.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1): + oxc-resolver@11.24.2: optionalDependencies: '@oxc-resolver/binding-android-arm-eabi': 11.24.2 '@oxc-resolver/binding-android-arm64': 11.24.2 @@ -16048,12 +17016,9 @@ snapshots: '@oxc-resolver/binding-linux-x64-gnu': 11.24.2 '@oxc-resolver/binding-linux-x64-musl': 11.24.2 '@oxc-resolver/binding-openharmony-arm64': 11.24.2 - '@oxc-resolver/binding-wasm32-wasi': 11.24.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@oxc-resolver/binding-wasm32-wasi': 11.24.2 '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' oxlint-plugin-react-doctor@0.9.12: dependencies: @@ -16400,18 +17365,18 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 - react-doctor@0.9.12(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(eslint@10.7.0(jiti@2.7.0)): + react-doctor@0.9.12(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(eslint@10.7.0(jiti@2.7.0)): dependencies: '@astrojs/compiler': 4.0.0 '@sentry/node': 10.67.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1)) agent-install: 0.0.5 conf: 15.1.0 confbox: 0.2.4 - deslop-js: 0.9.12(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + deslop-js: 0.9.12 eslint-plugin-react-hooks: 7.1.1(eslint@10.7.0(jiti@2.7.0)) jiti: 2.7.0 magicast: 0.5.3 - oxc-resolver: 11.24.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + oxc-resolver: 11.24.2 oxlint: 1.77.0 oxlint-plugin-react-doctor: 0.9.12 prompts: 2.4.2 @@ -16422,8 +17387,6 @@ snapshots: yaml: 2.9.0 yoga-layout: 3.2.1 transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' - eslint @@ -16513,7 +17476,7 @@ snapshots: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - react-scan@0.5.7(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(esbuild@0.27.7)(eslint@10.7.0(jiti@2.7.0))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)): + react-scan@0.5.7(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(esbuild@0.27.7)(eslint@10.7.0(jiti@2.7.0))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)): dependencies: '@babel/core': 7.29.7 '@babel/types': 7.29.7 @@ -16525,15 +17488,13 @@ snapshots: preact: 10.29.7 prompts: 2.4.2 react: 19.2.8 - react-doctor: 0.9.12(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(eslint@10.7.0(jiti@2.7.0)) + react-doctor: 0.9.12(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(eslint@10.7.0(jiti@2.7.0)) react-dom: 19.2.8(react@19.2.8) react-grab: 0.2.0(react@19.2.8) optionalDependencies: esbuild: 0.27.7 unplugin: 3.3.0(esbuild@0.27.7)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - '@farmfe/core' - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' @@ -16853,6 +17814,8 @@ snapshots: rope-sequence@1.3.4: {} + rou3@0.8.1: {} + router@2.2.0: dependencies: debug: 4.4.3 @@ -16944,6 +17907,12 @@ snapshots: transitivePeerDependencies: - supports-color + seroval-plugins@1.6.2(seroval@1.6.2): + dependencies: + seroval: 1.6.2 + + seroval@1.6.2: {} + serve-static@2.2.1: dependencies: encodeurl: 2.0.0 @@ -17200,6 +18169,8 @@ snapshots: space-separated-tokens@2.0.2: {} + srvx@0.11.22: {} + stack-generator@2.0.10: dependencies: stackframe: 1.3.4 @@ -17634,6 +18605,10 @@ snapshots: undici@7.28.0: {} + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 + unicorn-magic@0.3.0: {} unified@11.0.5: @@ -17694,6 +18669,24 @@ snapshots: vite: 8.1.5(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0) optional: true + unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)): + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.5 + webpack-virtual-modules: 0.6.2 + optionalDependencies: + esbuild: 0.28.1 + rolldown: 1.1.5 + rollup: 4.62.2 + vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0) + + unstorage@2.0.0-alpha.7(chokidar@5.0.0)(db0@0.3.4)(lru-cache@11.5.2)(ofetch@2.0.0-alpha.3): + optionalDependencies: + chokidar: 5.0.0 + db0: 0.3.4 + lru-cache: 11.5.2 + ofetch: 2.0.0-alpha.3 + update-browserslist-db@1.2.3(browserslist@4.28.7): dependencies: browserslist: 4.28.7 @@ -17814,6 +18807,10 @@ snapshots: tsx: 4.23.1 yaml: 2.9.0 + vitefu@1.1.3(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)): + optionalDependencies: + vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0) + vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 @@ -17922,8 +18919,7 @@ snapshots: - bufferutil - utf-8-validate - webpack-virtual-modules@0.6.2: - optional: true + webpack-virtual-modules@0.6.2: {} whatwg-mimetype@5.0.0: {} @@ -18006,6 +19002,13 @@ snapshots: xml-name-validator@5.0.0: {} + xmlbuilder2@4.0.3: + dependencies: + '@oozcitak/dom': 2.0.2 + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + js-yaml: 4.3.0 + xmlchars@2.2.0: {} yallist@3.1.1: {} From 8a990a34b8f1696cf97a0b18af86ccbac582f13a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 23 Aug 2026 08:05:12 +0000 Subject: [PATCH 2/4] feat(i18n): add locale routing and translations package `@vitnode/i18n` carries the locale logic for the TanStack Start app, built on `use-intl` so the existing `next-intl` message files and `useTranslations` call sites port over later without being rewritten. The default locale is served unprefixed (`/about`), every other locale is prefixed (`/pl/about`), and `/api/*` and `/admin/*` are never prefixed at all - they read the locale from the cookie instead. The cookie is the only persistence: `localStorage` is invisible to SSR and would flash the wrong language. Split into three cores so the bundler can keep them apart: `shared` is free of environment-specific imports and safe on both sides, `client` holds the URL rewriting and isomorphic detection, and `server` holds the request middleware that answers redirects before React starts rendering. The formatting time zone is pinned rather than inherited from the host, which otherwise differs between server and browser and turns every formatted date into a hydration mismatch. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WSPLXkitYKMftPVD4L3CVf --- packages/i18n/.gitignore | 1 + packages/i18n/eslint.config.mjs | 19 +++ packages/i18n/package.json | 58 ++++++++++ packages/i18n/src/core/client.ts | 104 +++++++++++++++++ packages/i18n/src/core/server.ts | 100 ++++++++++++++++ packages/i18n/src/core/shared.ts | 58 ++++++++++ packages/i18n/src/entry-client.tsx | 173 ++++++++++++++++++++++++++++ packages/i18n/src/entry-server.ts | 16 +++ packages/i18n/src/messages/en.ts | 21 ++++ packages/i18n/src/messages/index.ts | 19 +++ packages/i18n/src/messages/pl.ts | 21 ++++ packages/i18n/tsconfig.json | 13 +++ 12 files changed, 603 insertions(+) create mode 100644 packages/i18n/.gitignore create mode 100644 packages/i18n/eslint.config.mjs create mode 100644 packages/i18n/package.json create mode 100644 packages/i18n/src/core/client.ts create mode 100644 packages/i18n/src/core/server.ts create mode 100644 packages/i18n/src/core/shared.ts create mode 100644 packages/i18n/src/entry-client.tsx create mode 100644 packages/i18n/src/entry-server.ts create mode 100644 packages/i18n/src/messages/en.ts create mode 100644 packages/i18n/src/messages/index.ts create mode 100644 packages/i18n/src/messages/pl.ts create mode 100644 packages/i18n/tsconfig.json diff --git a/packages/i18n/.gitignore b/packages/i18n/.gitignore new file mode 100644 index 000000000..43370fa91 --- /dev/null +++ b/packages/i18n/.gitignore @@ -0,0 +1 @@ +*.tsbuildinfo diff --git a/packages/i18n/eslint.config.mjs b/packages/i18n/eslint.config.mjs new file mode 100644 index 000000000..13497eb72 --- /dev/null +++ b/packages/i18n/eslint.config.mjs @@ -0,0 +1,19 @@ +import eslintVitNode from "@vitnode/config/eslint"; +import eslintVitNodeReact from "@vitnode/config/eslint.react"; +import { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default [ + ...eslintVitNode, + ...eslintVitNodeReact, + { + languageOptions: { + parserOptions: { + project: "./tsconfig.json", + tsconfigRootDir: __dirname, + }, + }, + }, +]; diff --git a/packages/i18n/package.json b/packages/i18n/package.json new file mode 100644 index 000000000..36374caf2 --- /dev/null +++ b/packages/i18n/package.json @@ -0,0 +1,58 @@ +{ + "name": "@vitnode/i18n", + "version": "1.2.0-canary.78", + "description": "Locale routing and translations for VitNode on TanStack Start.", + "author": "VitNode Team", + "license": "MIT", + "homepage": "https://vitnode.com", + "repository": { + "type": "git", + "url": "git+https://github.com/aXenDeveloper/vitnode.git", + "directory": "packages/i18n" + }, + "keywords": [ + "vitnode", + "i18n", + "tanstack-start", + "use-intl" + ], + "type": "module", + "exports": { + "./client": { + "import": "./src/entry-client.tsx", + "default": "./src/entry-client.tsx" + }, + "./server": { + "import": "./src/entry-server.ts", + "default": "./src/entry-server.ts" + }, + "./messages": { + "import": "./src/messages/index.ts", + "default": "./src/messages/index.ts" + } + }, + "scripts": { + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "use-intl": "^4.13.7" + }, + "devDependencies": { + "@tanstack/react-query": "^5.101.4", + "@tanstack/react-router": "1.170.31", + "@tanstack/react-start": "1.168.48", + "@types/react": "^19.2.17", + "@vitnode/config": "workspace:*", + "eslint": "^10.7.0", + "react": "^19.2.8", + "typescript": "^6.0.3" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.x.x", + "@tanstack/react-router": "^1.x.x", + "@tanstack/react-start": "^1.x.x", + "react": "^19.2.x" + } +} diff --git a/packages/i18n/src/core/client.ts b/packages/i18n/src/core/client.ts new file mode 100644 index 000000000..d97a2ef48 --- /dev/null +++ b/packages/i18n/src/core/client.ts @@ -0,0 +1,104 @@ +import { createIsomorphicFn } from "@tanstack/react-start"; +import { getRequest } from "@tanstack/react-start/server"; + +import type { Locale } from "./shared"; + +import { + defaultLocale, + extractLocaleFromPath, + isValidLocale, + LOCALE_COOKIE, + shouldIgnorePath, + stripLocaleSegment, +} from "./shared"; + +/** + * The locale for the current request or document. + * + * Both branches follow the same rule so server and client always agree - a + * disagreement here surfaces as a hydration mismatch: + * + * - ignored paths (`/admin`, `/api`) carry no prefix, so the cookie decides + * - every other path is described entirely by its URL + * + * `createIsomorphicFn` is compiled away per environment, which is what keeps + * the server-only `getRequest` import out of the browser bundle. + */ +export const getCurrentLocale = createIsomorphicFn() + .server((): Locale => { + const url = new URL(getRequest().url); + + if (shouldIgnorePath(url.pathname)) { + return readCookieLocale(getRequest().headers.get("cookie")); + } + + return extractLocaleFromPath(url.pathname) ?? defaultLocale; + }) + .client((): Locale => { + const { pathname } = window.location; + + if (shouldIgnorePath(pathname)) { + return readCookieLocale(document.cookie); + } + + return extractLocaleFromPath(pathname) ?? defaultLocale; + }); + +/** Reads `LOCALE_COOKIE` out of a `Cookie` header or `document.cookie`. */ +export const readCookieLocale = (header: null | string): Locale => { + const value = header + ?.split(";") + .map(part => part.trim()) + .find(part => part.startsWith(`${LOCALE_COOKIE}=`)) + ?.slice(LOCALE_COOKIE.length + 1); + + return isValidLocale(value) ? value : defaultLocale; +}; + +/** + * Turns a public URL into the one the router matches against: `/pl/about` + * becomes `/about`, so the route tree never carries a locale segment. + */ +export const deLocalizeUrl = (url: URL): URL => { + if (shouldIgnorePath(url.pathname)) return url; + + const locale = extractLocaleFromPath(url.pathname); + + if (!locale) return url; + + const next = new URL(url); + next.pathname = stripLocaleSegment(url.pathname, locale); + + return next; +}; + +/** + * The inverse: turns a router URL into the public one, so `` + * renders `/pl/about` while the visitor is reading Polish. + * + * The router derives every href it commits to history through this, so it has + * to stay an exact inverse of `deLocalizeUrl` or navigation will bounce. + */ +export const localizeUrl = (url: URL): URL => { + if (shouldIgnorePath(url.pathname)) return url; + + const locale = getCurrentLocale(); + + if (locale === defaultLocale) return url; + + const next = new URL(url); + next.pathname = `/${locale}${url.pathname === "/" ? "" : url.pathname}`; + + return next; +}; + +/** The public path a given locale would serve `pathname` at. */ +export const localizedPath = (pathname: string, locale: Locale): string => { + if (shouldIgnorePath(pathname)) return pathname; + + const base = deLocalizeUrl(new URL(pathname, "http://localhost")).pathname; + + if (locale === defaultLocale) return base; + + return `/${locale}${base === "/" ? "" : base}`; +}; diff --git a/packages/i18n/src/core/server.ts b/packages/i18n/src/core/server.ts new file mode 100644 index 000000000..0648542de --- /dev/null +++ b/packages/i18n/src/core/server.ts @@ -0,0 +1,100 @@ +import { createMiddleware } from "@tanstack/react-start"; +import { setCookie } from "@tanstack/react-start/server"; + +import type { Locale } from "./shared"; + +import { + defaultLocale, + isValidLocale, + LOCALE_COOKIE, + shouldIgnorePath, + stripLocaleSegment, +} from "./shared"; + +/** A year, in seconds. */ +const COOKIE_MAX_AGE = 60 * 60 * 24 * 365; + +export interface LocaleResolution { + /** Where to send the visitor instead of rendering, if anywhere. */ + redirect?: string; + /** The locale the URL states outright, which the cookie should catch up to. */ + setCookie?: Locale; +} + +/** + * Decides what a request's URL means for the locale, before React renders. + * + * This has to run at the request layer rather than in a route: a route can + * throw `notFound()`, but by the time it runs the response has already started + * streaming, so it can no longer answer with a redirect. + * + * In order: + * + * 1. ignored paths are left alone - they are never prefixed + * 2. `/en/*` is not canonical, since the default locale has no prefix + * 3. a prefix in front of an ignored path is dropped (`/pl/admin` -> `/admin`) + * 4. otherwise the URL names a locale, so the cookie is brought in line + */ +const serializeLocaleCookie = (locale: Locale): string => + `${LOCALE_COOKIE}=${locale}; Path=/; Max-Age=${COOKIE_MAX_AGE}; SameSite=Lax`; + +export const handleLocaleMiddleware = (request: Request): LocaleResolution => { + const { pathname, search } = new URL(request.url); + + if (shouldIgnorePath(pathname)) return {}; + + const segment = pathname.split("/")[1]; + + if (segment === defaultLocale) { + return { + redirect: stripLocaleSegment(pathname, defaultLocale) + search, + setCookie: defaultLocale, + }; + } + + // An unprefixed public path is the default locale stated outright, so the + // cookie follows it too - otherwise switching back to English would leave + // `/admin` reading a stale locale. + if (!isValidLocale(segment)) return { setCookie: defaultLocale }; + + const stripped = stripLocaleSegment(pathname, segment); + + if (shouldIgnorePath(stripped)) { + return { redirect: stripped + search, setCookie: segment }; + } + + return { setCookie: segment }; +}; + +/** + * Applies `handleLocaleMiddleware` to every incoming request. + * + * Registered through `createStart` so it runs ahead of the router, which is the + * only place a redirect is still possible. + */ +export const localeRequestMiddleware = createMiddleware({ + type: "request", +}).server(async ({ next, request }) => { + const { redirect, setCookie: locale } = handleLocaleMiddleware(request); + + // A redirect is returned as its own Response, which never passes through the + // response context `setCookie` writes to - so it carries the header itself. + if (redirect !== undefined) { + const headers = new Headers({ location: redirect }); + + if (locale) headers.append("set-cookie", serializeLocaleCookie(locale)); + + return new Response(null, { headers, status: 301 }); + } + + if (locale) { + setCookie(LOCALE_COOKIE, locale, { + httpOnly: false, + maxAge: COOKIE_MAX_AGE, + path: "/", + sameSite: "lax", + }); + } + + return await next(); +}); diff --git a/packages/i18n/src/core/shared.ts b/packages/i18n/src/core/shared.ts new file mode 100644 index 000000000..345478d0e --- /dev/null +++ b/packages/i18n/src/core/shared.ts @@ -0,0 +1,58 @@ +/** + * Configuration and pure helpers shared by the client and the server. + * + * Nothing here may import an environment-specific module: both `./client` and + * `./server` pull this file in, so a `node:*` or a `window` reference would + * leak into whichever bundle it does not belong to. + */ + +export const defaultLocale = "en"; +export const supportedLocales = ["en", "pl"] as const; +export const LOCALE_COOKIE = "locale"; + +/** + * Formatting time zone. + * + * Pinned rather than left to the environment: `use-intl` would otherwise fall + * back to the host's zone, which differs between the server and the browser and + * makes every formatted date a hydration mismatch waiting to happen. + */ +export const defaultTimeZone = "UTC"; + +/** `/api/*` and `/admin/*` are never localized - they read the cookie. */ +export const ignoredPathsRegex = /^\/(?:api|admin)(?:\/|$)/; + +export type Locale = (typeof supportedLocales)[number]; + +export const isValidLocale = (locale: string | undefined): locale is Locale => + locale !== undefined && + (supportedLocales as readonly string[]).includes(locale); + +export const shouldIgnorePath = (pathname: string): boolean => + ignoredPathsRegex.test(pathname); + +/** + * The locale a pathname carries as its first segment, or `null` when it carries + * none. + * + * The default locale is deliberately reported as `null`: it is served without a + * prefix, so `/en/about` is not a canonical URL and gets redirected rather than + * treated as English. + */ +export const extractLocaleFromPath = (pathname: string): Locale | null => { + const segment = pathname.split("/")[1]; + + if (!isValidLocale(segment) || segment === defaultLocale) return null; + + return segment; +}; + +/** Drops the leading locale segment, keeping the result rooted at `/`. */ +export const stripLocaleSegment = ( + pathname: string, + locale: string, +): string => { + const rest = pathname.slice(locale.length + 1); + + return rest === "" ? "/" : rest; +}; diff --git a/packages/i18n/src/entry-client.tsx b/packages/i18n/src/entry-client.tsx new file mode 100644 index 000000000..f092b0001 --- /dev/null +++ b/packages/i18n/src/entry-client.tsx @@ -0,0 +1,173 @@ +import type { ComponentProps, ReactNode } from "react"; + +import { + keepPreviousData, + queryOptions, + useQuery, +} from "@tanstack/react-query"; +import { + Link, + type RegisteredRouter, + useRouterState, +} from "@tanstack/react-router"; +import { IntlProvider } from "use-intl"; + +import type { Locale } from "./core/shared"; +import type { Messages } from "./messages"; + +import { getCurrentLocale, localizedPath } from "./core/client"; +import { + defaultLocale, + defaultTimeZone, + isValidLocale, + shouldIgnorePath, + supportedLocales, +} from "./core/shared"; +import { loadMessages } from "./messages"; + +export { + deLocalizeUrl, + getCurrentLocale, + localizedPath, + localizeUrl, + readCookieLocale, +} from "./core/client"; +export { + defaultLocale, + defaultTimeZone, + extractLocaleFromPath, + ignoredPathsRegex, + isValidLocale, + type Locale, + LOCALE_COOKIE, + shouldIgnorePath, + supportedLocales, +} from "./core/shared"; +export { loadMessages, type Messages } from "./messages"; + +/** + * Messages never change for the lifetime of the tab, so once a locale has been + * fetched there is nothing to revalidate or evict. + */ +export const messagesQueryOptions = (locale: Locale) => + queryOptions({ + gcTime: Infinity, + queryFn: async () => await loadMessages(locale), + queryKey: ["i18n", "messages", locale] as const, + staleTime: Infinity, + }); + +/** The public pathname currently on screen, locale prefix included. */ +const usePublicPathname = (): string => + useRouterState({ + select: state => + new URL(state.location.publicHref, "http://localhost").pathname, + }); + +/** + * The locale currently on screen. + * + * `useRouterState` is what makes this reactive: the locale lives in the public + * URL rather than in the router's own path space, so a navigation is what has + * to trigger the re-read. + * + * Ignored paths carry no prefix, so there the cookie is the only source - which + * is exactly what `getCurrentLocale` reads on both sides of the wire. + */ +export const useLocale = (): Locale => { + const pathname = usePublicPathname(); + + if (shouldIgnorePath(pathname)) return getCurrentLocale(); + + const segment = pathname.split("/")[1]; + + return isValidLocale(segment) ? segment : defaultLocale; +}; + +export interface I18nProviderProps { + children: ReactNode; + /** + * The locale and messages the server rendered with. + * + * Passing them in is what keeps the first client render identical to the SSR + * output: the query starts already resolved instead of suspending and + * swapping the strings in afterwards. + */ + initial: { locale: Locale; messages: Messages }; +} + +export const I18nProvider = ({ children, initial }: I18nProviderProps) => { + const locale = useLocale(); + const { data } = useQuery({ + ...messagesQueryOptions(locale), + initialData: locale === initial.locale ? initial.messages : undefined, + // Keeps the previous locale's strings up for the one render a newly chosen + // locale takes to load, rather than blanking the page. + placeholderData: keepPreviousData, + }); + + return ( + + {children} + + ); +}; + +type AppFileRouteTypes = + RegisteredRouter["routeTree"]["types"]["fileRouteTypes"]; + +type AppTo = AppFileRouteTypes extends { to: infer TTo extends string } + ? TTo + : never; + +/** Every route target except the ones that are never given a locale prefix. */ +export type LocalizedTo = Exclude; + +export type LocalizedLinkProps = Omit, "to"> & { + to: LocalizedTo; +}; + +/** + * `Link`, narrowed to the routes that actually get localized. + * + * Targets are inferred from the app's generated `FileRouteTypes`, so an unknown + * path is a type error - and so is `/admin`, which stays a plain ``. + * + * The href itself needs no work here: the router's `output` rewrite is what + * turns `/about` into `/pl/about`. + */ +export const LocalizedLink = ({ to, ...props }: LocalizedLinkProps) => ( + +); + +/** + * Switches locale. + * + * This navigates the document rather than the router on purpose. The locale is + * server-owned state - it lives in the URL prefix and in the cookie - so a real + * request is what moves all of it at once, and it keeps working with JavaScript + * disabled. The request middleware syncs the cookie on the way through. + */ +export const LocaleSwitcher = () => { + const locale = useLocale(); + const pathname = usePublicPathname(); + + return ( + + ); +}; diff --git a/packages/i18n/src/entry-server.ts b/packages/i18n/src/entry-server.ts new file mode 100644 index 000000000..aac6b1caa --- /dev/null +++ b/packages/i18n/src/entry-server.ts @@ -0,0 +1,16 @@ +export { + handleLocaleMiddleware, + localeRequestMiddleware, + type LocaleResolution, +} from "./core/server"; +export { + defaultLocale, + defaultTimeZone, + extractLocaleFromPath, + ignoredPathsRegex, + isValidLocale, + type Locale, + LOCALE_COOKIE, + shouldIgnorePath, + supportedLocales, +} from "./core/shared"; diff --git a/packages/i18n/src/messages/en.ts b/packages/i18n/src/messages/en.ts new file mode 100644 index 000000000..d7a9ab514 --- /dev/null +++ b/packages/i18n/src/messages/en.ts @@ -0,0 +1,21 @@ +export const en = { + admin: { + description: "This area is never localized through the URL.", + title: "Admin", + }, + home: { + admin: "Admin", + description: "A TanStack Start application.", + title: "VitNode", + }, + notFound: { + title: "Page not found", + }, + locale: { + en: "English", + label: "Language", + pl: "Polski", + }, +}; + +export type Messages = typeof en; diff --git a/packages/i18n/src/messages/index.ts b/packages/i18n/src/messages/index.ts new file mode 100644 index 000000000..2366b4b14 --- /dev/null +++ b/packages/i18n/src/messages/index.ts @@ -0,0 +1,19 @@ +import type { Locale } from "../core/shared"; +import type { Messages } from "./en"; + +export type { Messages } from "./en"; + +/** + * Loads one locale's messages. + * + * The imports are dynamic so each locale becomes its own chunk - an English + * visitor never downloads the Polish strings. + */ +export const loadMessages = async (locale: Locale): Promise => { + switch (locale) { + case "en": + return (await import("./en")).en; + case "pl": + return (await import("./pl")).pl; + } +}; diff --git a/packages/i18n/src/messages/pl.ts b/packages/i18n/src/messages/pl.ts new file mode 100644 index 000000000..ce79e517b --- /dev/null +++ b/packages/i18n/src/messages/pl.ts @@ -0,0 +1,21 @@ +import type { Messages } from "./en"; + +export const pl: Messages = { + admin: { + description: "Ta sekcja nigdy nie jest tłumaczona przez adres URL.", + title: "Panel administracyjny", + }, + home: { + admin: "Panel administracyjny", + description: "Aplikacja oparta na TanStack Start.", + title: "VitNode", + }, + notFound: { + title: "Nie znaleziono strony", + }, + locale: { + en: "English", + label: "Język", + pl: "Polski", + }, +}; diff --git a/packages/i18n/tsconfig.json b/packages/i18n/tsconfig.json new file mode 100644 index 000000000..1167de4de --- /dev/null +++ b/packages/i18n/tsconfig.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vitnode/config/tsconfig", + "compilerOptions": { + "target": "ESNext", + "module": "esnext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "noEmit": true + }, + "include": ["**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} From 6eb2cc533ade19aebbc34d9205466e393becadc5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 23 Aug 2026 08:05:23 +0000 Subject: [PATCH 3/4] feat(web): wire locale routing into the Start app The locale lives only in the public URL: `rewrite.input` strips the prefix before the router matches, and `rewrite.output` puts it back on every href the router builds. So the route tree holds plain paths, and a `` renders `/pl` on a Polish page without any route knowing that locales exist. An unknown prefix like `/xyz` is left alone, matches nothing and 404s, rather than quietly falling back to English. `src/start.ts` registers the locale request middleware, which is what makes `/en/*` and `/pl/admin` answer with a 301 - a route can throw `notFound()`, but by then the response has already started streaming. The root loader resolves the locale and returns its messages, so they travel to the client in the router's own SSR payload. The first client render is identical to the server's, and an English visitor never downloads the Polish chunk. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WSPLXkitYKMftPVD4L3CVf --- apps/web/package.json | 4 +++- apps/web/src/routeTree.gen.ts | 3 ++- apps/web/src/router.tsx | 19 +++++++++++++++++ apps/web/src/routes/__root.tsx | 24 ++++++++++++++++++++-- apps/web/src/routes/admin.tsx | 14 +++++++++++-- apps/web/src/routes/index.tsx | 22 ++++++++++++++++++-- apps/web/src/start.ts | 6 ++++++ pnpm-lock.yaml | 37 ++++++++++++++++++++++++++++++++++ 8 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 apps/web/src/start.ts diff --git a/apps/web/package.json b/apps/web/package.json index d54010ea1..879035833 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -16,8 +16,10 @@ "@tanstack/react-query": "^5.101.4", "@tanstack/react-router": "1.170.31", "@tanstack/react-start": "1.168.48", + "@vitnode/i18n": "workspace:*", "react": "^19.2.8", - "react-dom": "^19.2.8" + "react-dom": "^19.2.8", + "use-intl": "^4.13.7" }, "devDependencies": { "@tailwindcss/vite": "^4.3.3", diff --git a/apps/web/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts index 74f928420..219a365aa 100644 --- a/apps/web/src/routeTree.gen.ts +++ b/apps/web/src/routeTree.gen.ts @@ -77,10 +77,11 @@ export const routeTree = rootRouteImport ._addFileTypes() import type { getRouter } from './router.tsx' -import type { createStart } from '@tanstack/react-start' +import type { startInstance } from './start.ts' declare module '@tanstack/react-start' { interface Register { ssr: true router: Awaited> + config: Awaited> } } diff --git a/apps/web/src/router.tsx b/apps/web/src/router.tsx index 1131a2993..2bf335896 100644 --- a/apps/web/src/router.tsx +++ b/apps/web/src/router.tsx @@ -1,5 +1,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { createRouter } from "@tanstack/react-router"; +import { deLocalizeUrl, localizeUrl } from "@vitnode/i18n/client"; +import { useTranslations } from "use-intl"; import { routeTree } from "./routeTree.gen"; @@ -7,6 +9,16 @@ export interface RouterAppContext { queryClient: QueryClient; } +const NotFound = () => { + const t = useTranslations("notFound"); + + return ( +
+

{t("title")}

+
+ ); +}; + export const getRouter = () => { const queryClient = new QueryClient(); @@ -15,6 +27,13 @@ export const getRouter = () => { context: { queryClient } satisfies RouterAppContext, scrollRestoration: true, defaultPreload: "intent", + defaultNotFoundComponent: NotFound, + // The locale lives only in the public URL: the router matches, and app code + // links against, paths with no locale segment at all. + rewrite: { + input: ({ url }) => deLocalizeUrl(url), + output: ({ url }) => localizeUrl(url), + }, Wrap: ({ children }) => ( {children} ), diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index f71ebfb50..b780e5e0d 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -5,11 +5,29 @@ import { HeadContent, Scripts, } from "@tanstack/react-router"; +import { + getCurrentLocale, + I18nProvider, + messagesQueryOptions, +} from "@vitnode/i18n/client"; import appCss from "../styles/globals.css?url"; export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()( { + // The locale comes from the request, not from a route param, so the loader + // is what resolves it - and returning the messages hands them to the client + // through the router's own SSR payload, with nothing to re-fetch on hydrate. + loader: async ({ context }) => { + const locale = getCurrentLocale(); + + return { + locale, + messages: await context.queryClient.ensureQueryData( + messagesQueryOptions(locale), + ), + }; + }, head: () => ({ meta: [ { charSet: "utf-8" }, @@ -23,13 +41,15 @@ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()( ); function RootDocument({ children }: { children: React.ReactNode }) { + const initial = Route.useLoaderData(); + return ( - + - {children} + {children} diff --git a/apps/web/src/routes/admin.tsx b/apps/web/src/routes/admin.tsx index a614b4707..918d0394e 100644 --- a/apps/web/src/routes/admin.tsx +++ b/apps/web/src/routes/admin.tsx @@ -1,13 +1,23 @@ -import { createFileRoute } from "@tanstack/react-router"; +import { createFileRoute, Link } from "@tanstack/react-router"; +import { useTranslations } from "use-intl"; export const Route = createFileRoute("/admin")({ component: Admin, }); function Admin() { + const t = useTranslations("admin"); + return (
-

Admin

+

{t("title")}

+

+ {t("description")} +

+ {/* Never localized, so a plain Link rather than a LocalizedLink. */} + + {t("title")} +
); } diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index 73049963b..479c922de 100644 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -1,13 +1,31 @@ -import { createFileRoute } from "@tanstack/react-router"; +import { createFileRoute, Link } from "@tanstack/react-router"; +import { LocaleSwitcher, LocalizedLink } from "@vitnode/i18n/client"; +import { useTranslations } from "use-intl"; export const Route = createFileRoute("/")({ component: Home, }); function Home() { + const t = useTranslations("home"); + return (
-

VitNode

+

{t("title")}

+

+ {t("description")} +

+ +
+ {/* Localized: picks up the current locale's prefix. */} + + {t("title")} + + {/* Never localized, so a plain Link. */} + + {t("admin")} + +
); } diff --git a/apps/web/src/start.ts b/apps/web/src/start.ts new file mode 100644 index 000000000..d28ada435 --- /dev/null +++ b/apps/web/src/start.ts @@ -0,0 +1,6 @@ +import { createStart } from "@tanstack/react-start"; +import { localeRequestMiddleware } from "@vitnode/i18n/server"; + +export const startInstance = createStart(() => ({ + requestMiddleware: [localeRequestMiddleware], +})); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f27d949ed..7346a49f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -280,12 +280,18 @@ importers: '@tanstack/react-start': specifier: 1.168.48 version: 1.168.48(crossws@0.4.12(srvx@0.11.22))(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + '@vitnode/i18n': + specifier: workspace:* + version: link:../../packages/i18n react: specifier: ^19.2.8 version: 19.2.8 react-dom: specifier: ^19.2.8 version: 19.2.8(react@19.2.8) + use-intl: + specifier: ^4.13.7 + version: 4.13.7(react@19.2.8) devDependencies: '@tailwindcss/vite': specifier: ^4.3.3 @@ -429,6 +435,37 @@ importers: specifier: ^6.0.3 version: 6.0.3 + packages/i18n: + dependencies: + use-intl: + specifier: ^4.13.7 + version: 4.13.7(react@19.2.8) + devDependencies: + '@tanstack/react-query': + specifier: ^5.101.4 + version: 5.101.4(react@19.2.8) + '@tanstack/react-router': + specifier: 1.170.31 + version: 1.170.31(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/react-start': + specifier: 1.168.48 + version: 1.168.48(crossws@0.4.12(srvx@0.11.22))(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) + '@types/react': + specifier: ^19.2.17 + version: 19.2.17 + '@vitnode/config': + specifier: workspace:* + version: link:../config + eslint: + specifier: ^10.7.0 + version: 10.7.0(jiti@2.7.0) + react: + specifier: ^19.2.8 + version: 19.2.8 + typescript: + specifier: ^6.0.3 + version: 6.0.3 + packages/node-cron: dependencies: node-cron: From dcc6db1a8c25b196ce8566ca3c687d3edcd91110 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 23 Aug 2026 08:05:30 +0000 Subject: [PATCH 4/4] build: register apps/web and packages/i18n in the pipeline `pnpm-workspace.yaml` needs no change - its `apps/*` and `packages/*` globs already cover both new workspaces. Adds a `typecheck` task, which both new workspaces expose, and `.output` to the cached build outputs so Turborepo knows what `vite build` leaves behind. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WSPLXkitYKMftPVD4L3CVf --- turbo.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/turbo.json b/turbo.json index 721572f01..53c5cad70 100644 --- a/turbo.json +++ b/turbo.json @@ -31,7 +31,13 @@ "build": { "dependsOn": ["^build:plugins", "^build"], "inputs": ["$TURBO_DEFAULT$", ".env*"], - "outputs": [".next/**", "!.next/cache/**", "!.next/dev/**", "dist/**"], + "outputs": [ + ".next/**", + "!.next/cache/**", + "!.next/dev/**", + ".output/**", + "dist/**" + ], "env": ["POSTGRES_URL", "NEXT_PUBLIC_API_URL", "NEXT_PUBLIC_WEB_URL"] }, "build:scripts": { @@ -48,6 +54,9 @@ "lint": { "dependsOn": ["^lint"] }, + "typecheck": { + "dependsOn": ["^typecheck"] + }, "lint:fix": { "dependsOn": ["^lint:fix"] },