diff --git a/.changeset/react-router-integration.md b/.changeset/react-router-integration.md
new file mode 100644
index 00000000..71052aa5
--- /dev/null
+++ b/.changeset/react-router-integration.md
@@ -0,0 +1,5 @@
+---
+"evlog": minor
+---
+
+Add React Router middleware integration (`evlog/react-router`) with automatic wide-event logging, drain, enrich, and tail sampling support
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 33e55f4b..9ec034ba 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -34,6 +34,7 @@ Here are the available types and scopes:
- elysia (Elysia plugin)
- fastify (Fastify plugin)
- nestjs (NestJS middleware)
+- react-router (React Router integration)
- sveltekit (SvelteKit integration)
- workers (Cloudflare Workers adapter)
- fs (File System adapter)
diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml
index 0f26bcef..793989ee 100644
--- a/.github/workflows/semantic-pull-request.yml
+++ b/.github/workflows/semantic-pull-request.yml
@@ -39,6 +39,7 @@ jobs:
elysia
fastify
nestjs
+ react-router
sveltekit
workers
fs
diff --git a/apps/docs/app/components/features/FeatureFrameworks.vue b/apps/docs/app/components/features/FeatureFrameworks.vue
index 65656c78..49029732 100644
--- a/apps/docs/app/components/features/FeatureFrameworks.vue
+++ b/apps/docs/app/components/features/FeatureFrameworks.vue
@@ -23,15 +23,16 @@ const frameworkRows = [
{ name: 'SvelteKit', icon: 'i-simple-icons-svelte', tab: 2 },
{ name: 'Nitro', icon: 'i-custom-nitro', tab: 3 },
{ name: 'TanStack Start', icon: 'i-custom-tanstack', tab: 4 },
- { name: 'NestJS', icon: 'i-simple-icons-nestjs', tab: 5 },
+ { name: 'React Router', icon: 'i-simple-icons-reactrouter', tab: 5 },
+ { name: 'NestJS', icon: 'i-simple-icons-nestjs', tab: 6 },
],
[
- { name: 'Express', icon: 'i-simple-icons-express', tab: 6 },
- { name: 'Hono', icon: 'i-simple-icons-hono', tab: 7 },
- { name: 'Fastify', icon: 'i-simple-icons-fastify', tab: 8 },
- { name: 'Elysia', icon: 'i-custom-elysia', tab: 9 },
- { name: 'Cloudflare', icon: 'i-simple-icons-cloudflare', tab: 10 },
- { name: 'Bun', icon: 'i-simple-icons-bun', tab: 11 },
+ { name: 'Express', icon: 'i-simple-icons-express', tab: 7 },
+ { name: 'Hono', icon: 'i-simple-icons-hono', tab: 8 },
+ { name: 'Fastify', icon: 'i-simple-icons-fastify', tab: 9 },
+ { name: 'Elysia', icon: 'i-custom-elysia', tab: 10 },
+ { name: 'Cloudflare', icon: 'i-simple-icons-cloudflare', tab: 11 },
+ { name: 'Bun', icon: 'i-simple-icons-bun', tab: 12 },
{ name: 'Vite', icon: 'i-custom-vite', link: '/core-concepts/vite-plugin' },
],
]
@@ -129,24 +130,27 @@ const frameworkRows = [
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
diff --git a/apps/docs/content/0.landing.md b/apps/docs/content/0.landing.md
index 4aded7cc..3bef80ce 100644
--- a/apps/docs/content/0.landing.md
+++ b/apps/docs/content/0.landing.md
@@ -280,6 +280,34 @@ Wide events and structured errors for TypeScript. One log per request, full cont
})
```
+ #react-router
+ ```ts [app/routes/api.checkout.tsx]
+ import { loggerContext } from 'evlog/react-router'
+ import { createError } from 'evlog'
+
+ export async function action({ request, context }: Route.ActionArgs) {
+ const log = context.get(loggerContext)
+ const { cartId } = await request.json()
+
+ const cart = await db.findCart(cartId)
+ log.set({ cart: { items: cart.items.length, total: cart.total } })
+
+ const charge = await stripe.charge(cart.total)
+ log.set({ stripe: { chargeId: charge.id } })
+
+ if (!charge.success) {
+ throw createError({
+ status: 402,
+ message: 'Payment failed',
+ why: charge.decline_reason,
+ fix: 'Try a different payment method',
+ })
+ }
+
+ return Response.json({ orderId: charge.id })
+ }
+ ```
+
#nestjs
```ts [app.module.ts]
import { Module } from '@nestjs/common'
diff --git a/apps/docs/content/1.getting-started/2.installation.md b/apps/docs/content/1.getting-started/2.installation.md
index 0babeccc..6273b824 100644
--- a/apps/docs/content/1.getting-started/2.installation.md
+++ b/apps/docs/content/1.getting-started/2.installation.md
@@ -93,6 +93,15 @@ After installing the package, follow the setup guide for your framework:
:::
:::card
---
+ icon: i-simple-icons-reactrouter
+ title: React Router
+ to: /frameworks/react-router
+ color: neutral
+ ---
+ Middleware with `context.get(loggerContext)`.
+ :::
+ :::card
+ ---
icon: i-simple-icons-nestjs
title: NestJS
to: /frameworks/nestjs
diff --git a/apps/docs/content/2.frameworks/00.overview.md b/apps/docs/content/2.frameworks/00.overview.md
index 55b8ba49..e4e439f5 100644
--- a/apps/docs/content/2.frameworks/00.overview.md
+++ b/apps/docs/content/2.frameworks/00.overview.md
@@ -17,6 +17,7 @@ evlog provides native integrations for every major TypeScript framework. The sam
| [SvelteKit](/frameworks/sveltekit) | `evlog/sveltekit` | Hooks | `event.locals.log` / `useLogger()` | Stable |
| [Nitro](/frameworks/nitro) | `evlog/nitro` | Module | `useLogger(event)` | Stable |
| [TanStack Start](/frameworks/tanstack-start) | `evlog/nitro/v3` | Module | `useRequest().context.log` | Stable |
+| [React Router](/frameworks/react-router) | `evlog/react-router` | Middleware | `context.get(loggerContext)` / `useLogger()` | Stable |
| [NestJS](/frameworks/nestjs) | `evlog/nestjs` | Module | `useLogger()` | Stable |
| [Express](/frameworks/express) | `evlog/express` | Middleware | `req.log` / `useLogger()` | Stable |
| [Hono](/frameworks/hono) | `evlog/hono` | Middleware | `c.get('log')` | Stable |
@@ -77,6 +78,15 @@ evlog provides native integrations for every major TypeScript framework. The sam
:::
:::card
---
+ icon: i-simple-icons-reactrouter
+ title: React Router
+ to: /frameworks/react-router
+ color: neutral
+ ---
+ Middleware with `context.get(loggerContext)` and `useLogger()` for loaders and services.
+ :::
+ :::card
+ ---
icon: i-simple-icons-nestjs
title: NestJS
to: /frameworks/nestjs
diff --git a/apps/docs/content/2.frameworks/11.react-router.md b/apps/docs/content/2.frameworks/11.react-router.md
new file mode 100644
index 00000000..d142a4db
--- /dev/null
+++ b/apps/docs/content/2.frameworks/11.react-router.md
@@ -0,0 +1,303 @@
+---
+title: React Router
+description: Using evlog with React Router — automatic wide events, structured errors, drain adapters, enrichers, and tail sampling in React Router applications.
+links:
+ - label: Source Code
+ icon: i-simple-icons-github
+ to: https://github.com/HugoRCD/evlog/tree/main/examples/react-router
+ color: neutral
+ variant: subtle
+navigation:
+ title: React Router
+ icon: i-simple-icons-reactrouter
+---
+
+The `evlog/react-router` middleware auto-creates a request-scoped logger accessible via `context.get(loggerContext)` or `useLogger()` and emits a wide event when the response completes.
+
+::callout{icon="i-lucide-info" color="info"}
+React Router has three [modes](https://reactrouter.com/start/modes): **Framework**, **Data**, and **Declarative**. The `evlog/react-router` middleware requires the middleware API, which is available in **Framework** and **Data** modes only. Declarative mode does not support middleware — use [`evlog/browser`](/core-concepts/client-logging) for client-side logging instead.
+::
+
+::code-collapse
+```txt [Prompt]
+Set up evlog in my React Router app.
+
+- Install evlog: pnpm add evlog
+- Call initLogger({ env: { service: 'my-api' } }) at startup
+- Alternatively, use evlog/vite plugin in vite.config.ts for auto-init (replaces initLogger)
+- Enable middleware in react-router.config.ts: future: { v8_middleware: true }
+- Import evlog middleware and loggerContext from 'evlog/react-router'
+- Add evlog() to root route's middleware array
+- Access logger via context.get(loggerContext) in loaders/actions
+- Or use useLogger() from services without passing context
+- Optionally pass drain, enrich, include, and keep options to evlog()
+
+Docs: https://www.evlog.dev/frameworks/react-router
+Adapters: https://www.evlog.dev/adapters/overview
+```
+::
+
+## Quick Start
+
+### 1. Install
+
+```bash
+bun add evlog react-router @react-router/node @react-router/serve
+```
+
+### 2. Enable middleware
+
+```typescript [react-router.config.ts]
+import type { Config } from '@react-router/dev/config'
+
+export default {
+ future: {
+ v8_middleware: true,
+ },
+} satisfies Config
+```
+
+### 3. Initialize and register the middleware
+
+```typescript [app/root.tsx]
+import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'
+import { initLogger } from 'evlog'
+import { evlog } from 'evlog/react-router'
+
+initLogger({
+ env: { service: 'my-api' },
+})
+
+export const middleware: Route.MiddlewareFunction[] = [
+ evlog(),
+]
+
+export default function Root() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+```
+
+### 4. Use the logger in loaders
+
+```typescript [app/routes/health.tsx]
+import { loggerContext } from 'evlog/react-router'
+
+export async function loader({ context }: Route.LoaderArgs) {
+ const log = context.get(loggerContext)
+ log.set({ route: 'health' })
+ return { ok: true }
+}
+```
+
+::callout{color="info" icon="i-custom-vite"}
+**Using Vite?** The [`evlog/vite`](/core-concepts/vite-plugin) [plugin](/core-concepts/vite-plugin) replaces the `initLogger()` call with compile-time auto-initialization, strips `log.debug()` from production builds, and injects source locations.
+::
+
+The `loggerContext` provides typed access to the evlog logger in any loader or action via `context.get(loggerContext)`.
+
+## Wide Events
+
+Build up context progressively through your loader. One request = one wide event:
+
+```typescript [app/routes/users.$id.tsx]
+import { loggerContext } from 'evlog/react-router'
+
+export async function loader({ params, context }: Route.LoaderArgs) {
+ const log = context.get(loggerContext)
+ const userId = params.id
+
+ log.set({ user: { id: userId } })
+
+ const user = await db.findUser(userId)
+ log.set({ user: { name: user.name, plan: user.plan } })
+
+ const orders = await db.findOrders(userId)
+ log.set({ orders: { count: orders.length, totalRevenue: sum(orders) } })
+
+ return { user, orders }
+}
+```
+
+All fields are merged into a single wide event emitted when the request completes:
+
+```bash [Terminal output]
+14:58:15 INFO [my-api] GET /users/usr_123 200 in 12ms
+ ├─ orders: count=2 totalRevenue=6298
+ ├─ user: id=usr_123 name=Alice plan=pro
+ └─ requestId: 4a8ff3a8-...
+```
+
+## useLogger()
+
+Access the logger from any server-side function without passing context:
+
+```typescript [app/services/user.server.ts]
+import { useLogger } from 'evlog/react-router'
+
+export async function findUser(userId: string) {
+ const log = useLogger()
+ log.set({ db: { query: 'findUser', userId } })
+ return await db.users.find(userId)
+}
+```
+
+Then call the service from your loader — `useLogger()` returns the same logger instance:
+
+```typescript [app/routes/users.$id.tsx]
+import { loggerContext } from 'evlog/react-router'
+import { findUser } from '~/services/user.server'
+
+export async function loader({ params, context }: Route.LoaderArgs) {
+ const log = context.get(loggerContext)
+ log.set({ user: { id: params.id } })
+
+ const user = await findUser(params.id!)
+ return { user }
+}
+```
+
+## Error Handling
+
+Use `createError` for structured errors with `why`, `fix`, and `link` fields:
+
+```typescript [app/routes/checkout.tsx]
+import { loggerContext } from 'evlog/react-router'
+import { createError } from 'evlog'
+
+export async function loader({ context }: Route.LoaderArgs) {
+ const log = context.get(loggerContext)
+ log.set({ cart: { items: 3, total: 9999 } })
+
+ throw createError({
+ message: 'Payment failed',
+ status: 402,
+ why: 'Card declined by issuer',
+ fix: 'Try a different payment method',
+ link: 'https://docs.example.com/payments/declined',
+ })
+}
+```
+
+The error is captured and logged with both the custom context and structured error fields:
+
+```bash [Terminal output]
+14:58:20 ERROR [my-api] GET /checkout 402 in 3ms
+ ├─ error: name=EvlogError message=Payment failed status=402
+ ├─ cart: items=3 total=9999
+ └─ requestId: 880a50ac-...
+```
+
+## Configuration
+
+See the [Configuration reference](/core-concepts/configuration) for all available options (`initLogger`, middleware options, sampling, silent mode, etc.).
+
+## Drain & Enrichers
+
+Configure drain adapters and enrichers directly in the middleware options:
+
+```typescript [app/root.tsx]
+import { createAxiomDrain } from 'evlog/axiom'
+import { createUserAgentEnricher } from 'evlog/enrichers'
+
+const userAgent = createUserAgentEnricher()
+
+export const middleware: Route.MiddlewareFunction[] = [
+ evlog({
+ drain: createAxiomDrain(),
+ enrich: (ctx) => {
+ userAgent(ctx)
+ ctx.event.region = process.env.FLY_REGION
+ },
+ }),
+]
+```
+
+### Pipeline (Batching & Retry)
+
+For production, wrap your adapter with `createDrainPipeline` to batch events and retry on failure:
+
+```typescript [app/root.tsx]
+import type { DrainContext } from 'evlog'
+import { createAxiomDrain } from 'evlog/axiom'
+import { createDrainPipeline } from 'evlog/pipeline'
+
+const pipeline = createDrainPipeline({
+ batch: { size: 50, intervalMs: 5000 },
+ retry: { maxAttempts: 3 },
+})
+const drain = pipeline(createAxiomDrain())
+
+export const middleware: Route.MiddlewareFunction[] = [
+ evlog({ drain }),
+]
+```
+
+::callout{color="info" icon="i-lucide-info"}
+Call `drain.flush()` on server shutdown to ensure all buffered events are sent. See the [Pipeline docs](/adapters/pipeline) for all options.
+::
+
+## Tail Sampling
+
+Use `keep` to force-retain specific events regardless of head sampling:
+
+```typescript [app/root.tsx]
+export const middleware: Route.MiddlewareFunction[] = [
+ evlog({
+ drain: createAxiomDrain(),
+ keep: (ctx) => {
+ if (ctx.duration && ctx.duration > 2000) ctx.shouldKeep = true
+ },
+ }),
+]
+```
+
+## Route Filtering
+
+Control which routes are logged with `include` and `exclude` patterns:
+
+```typescript [app/root.tsx]
+export const middleware: Route.MiddlewareFunction[] = [
+ evlog({
+ include: ['/api/**'],
+ exclude: ['/_internal/**', '/health'],
+ routes: {
+ '/api/auth/**': { service: 'auth-service' },
+ '/api/payment/**': { service: 'payment-service' },
+ },
+ }),
+]
+```
+
+## Run Locally
+
+```bash
+git clone https://github.com/HugoRCD/evlog.git
+cd evlog
+bun install
+bun run example:react-router
+```
+
+Open to explore the interactive test UI.
+
+::card-group
+ :::card
+ ---
+ icon: i-simple-icons-github
+ title: Source Code
+ to: https://github.com/HugoRCD/evlog/tree/main/examples/react-router
+ ---
+ Browse the complete React Router example source on GitHub.
+ :::
+::
diff --git a/apps/docs/content/2.frameworks/11.cloudflare-workers.md b/apps/docs/content/2.frameworks/12.cloudflare-workers.md
similarity index 100%
rename from apps/docs/content/2.frameworks/11.cloudflare-workers.md
rename to apps/docs/content/2.frameworks/12.cloudflare-workers.md
diff --git a/apps/docs/content/2.frameworks/12.standalone.md b/apps/docs/content/2.frameworks/13.standalone.md
similarity index 100%
rename from apps/docs/content/2.frameworks/12.standalone.md
rename to apps/docs/content/2.frameworks/13.standalone.md
diff --git a/apps/docs/content/2.frameworks/13.astro.md b/apps/docs/content/2.frameworks/14.astro.md
similarity index 100%
rename from apps/docs/content/2.frameworks/13.astro.md
rename to apps/docs/content/2.frameworks/14.astro.md
diff --git a/apps/docs/content/2.frameworks/14.custom-integration.md b/apps/docs/content/2.frameworks/15.custom-integration.md
similarity index 100%
rename from apps/docs/content/2.frameworks/14.custom-integration.md
rename to apps/docs/content/2.frameworks/15.custom-integration.md
diff --git a/bun.lock b/bun.lock
index 967d6f55..9fb5abd7 100644
--- a/bun.lock
+++ b/bun.lock
@@ -177,6 +177,25 @@
"typescript": "^5.9.3",
},
},
+ "examples/react-router": {
+ "name": "evlog-react-router-example",
+ "dependencies": {
+ "@react-router/node": "^7.9.0",
+ "@react-router/serve": "^7.9.0",
+ "evlog": "workspace:*",
+ "isbot": "^5",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-router": "^7.9.0",
+ },
+ "devDependencies": {
+ "@react-router/dev": "^7.9.0",
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0",
+ "typescript": "^5.9.3",
+ "vite": "^6.0.0",
+ },
+ },
"examples/solidstart": {
"name": "evlog-solidstart-example",
"dependencies": {
@@ -275,6 +294,7 @@
"nitropack": "^2.13.1",
"nuxt": "^4.4.2",
"pino": "^10.3.1",
+ "react-router": "^7.9.0",
"supertest": "^7.2.2",
"tsdown": "^0.21.4",
"typescript": "^5.9.3",
@@ -296,6 +316,7 @@
"nitropack": "^2.13.1",
"ofetch": "^1.5.1",
"react": ">=19.2.4",
+ "react-router": ">=7.9.0",
"vite": "^7.0.0 || ^8.0.0",
},
"optionalPeers": [
@@ -312,6 +333,7 @@
"nitropack",
"ofetch",
"react",
+ "react-router",
"vite",
],
},
@@ -357,7 +379,7 @@
"@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@11.9.3", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ=="],
- "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
+ "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
"@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="],
@@ -401,12 +423,16 @@
"@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A=="],
+ "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.28.6", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA=="],
+
"@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
"@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
"@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw=="],
+ "@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="],
+
"@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="],
"@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
@@ -777,6 +803,8 @@
"@miyaneee/rollup-plugin-json5": ["@miyaneee/rollup-plugin-json5@1.2.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0", "json5": "^2.2.3" }, "peerDependencies": { "rollup": "^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" } }, "sha512-JjTIaXZp9WzhUHpElrqPnl1AzBi/rvRs065F71+aTmlqvTMVkdbjZ8vfFl4nRlgJy+TPBw69ZK4pwFdmOAt4aA=="],
+ "@mjackson/node-fetch-server": ["@mjackson/node-fetch-server@0.2.0", "", {}, "sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng=="],
+
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
@@ -1047,8 +1075,18 @@
"@quansync/fs": ["@quansync/fs@1.0.0", "", { "dependencies": { "quansync": "^1.0.0" } }, "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ=="],
+ "@react-router/dev": ["@react-router/dev@7.13.1", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/preset-typescript": "^7.27.1", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@react-router/node": "7.13.1", "@remix-run/node-fetch-server": "^0.13.0", "arg": "^5.0.1", "babel-dead-code-elimination": "^1.0.6", "chokidar": "^4.0.0", "dedent": "^1.5.3", "es-module-lexer": "^1.3.1", "exit-hook": "2.2.1", "isbot": "^5.1.11", "jsesc": "3.0.2", "lodash": "^4.17.21", "p-map": "^7.0.3", "pathe": "^1.1.2", "picocolors": "^1.1.1", "pkg-types": "^2.3.0", "prettier": "^3.6.2", "react-refresh": "^0.14.0", "semver": "^7.3.7", "tinyglobby": "^0.2.14", "valibot": "^1.2.0", "vite-node": "^3.2.2" }, "peerDependencies": { "@react-router/serve": "^7.13.1", "@vitejs/plugin-rsc": "~0.5.7", "react-router": "^7.13.1", "react-server-dom-webpack": "^19.2.3", "typescript": "^5.1.0", "vite": "^5.1.0 || ^6.0.0 || ^7.0.0", "wrangler": "^3.28.2 || ^4.0.0" }, "optionalPeers": ["@react-router/serve", "@vitejs/plugin-rsc", "react-server-dom-webpack", "typescript", "wrangler"], "bin": { "react-router": "bin.js" } }, "sha512-H+kEvbbOaWGaitOyL6CgqPsHqRUh66HuVRvIEaZEqdoAY/1xChdhmmq6ZumMHzcFHgHlfOcoXgNHlz6ZO4NWcg=="],
+
+ "@react-router/express": ["@react-router/express@7.13.1", "", { "dependencies": { "@react-router/node": "7.13.1" }, "peerDependencies": { "express": "^4.17.1 || ^5", "react-router": "7.13.1", "typescript": "^5.1.0" }, "optionalPeers": ["typescript"] }, "sha512-ujHom4LiEWsbnohNArwNT86QP3WRB5p+rY8AAll6s4gdrzgOXIy3FHDc3up5Lz8juUrZKh0d+B+PZa/IdDSK3A=="],
+
+ "@react-router/node": ["@react-router/node@7.13.1", "", { "dependencies": { "@mjackson/node-fetch-server": "^0.2.0" }, "peerDependencies": { "react-router": "7.13.1", "typescript": "^5.1.0" }, "optionalPeers": ["typescript"] }, "sha512-IWPPf+Q3nJ6q4bwyTf5leeGUfg8GAxSN1RKj5wp9SK915zKK+1u4TCOfOmr8hmC6IW1fcjKV0WChkM0HkReIiw=="],
+
+ "@react-router/serve": ["@react-router/serve@7.13.1", "", { "dependencies": { "@mjackson/node-fetch-server": "^0.2.0", "@react-router/express": "7.13.1", "@react-router/node": "7.13.1", "compression": "^1.8.1", "express": "^4.19.2", "get-port": "5.1.1", "morgan": "^1.10.1", "source-map-support": "^0.5.21" }, "peerDependencies": { "react-router": "7.13.1" }, "bin": { "react-router-serve": "bin.js" } }, "sha512-vh5lr41rioXLz/zNLTYo0zq4yh97AkgEkJK7bhPeXnNbLNtI36WCZ2AeBtSJ4sdx4gx5LZvcjP8zoWFfSbNupA=="],
+
"@remirror/core-constants": ["@remirror/core-constants@3.0.0", "", {}, "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg=="],
+ "@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.13.0", "", {}, "sha512-1EsNo0ZpgXu/90AWoRZf/oE3RVTUS80tiTUpt+hv5pjtAkw7icN4WskDwz/KdAw5ARbJLMhZBrO1NqThmy/McA=="],
+
"@resvg/resvg-js": ["@resvg/resvg-js@2.6.2", "", { "optionalDependencies": { "@resvg/resvg-js-android-arm-eabi": "2.6.2", "@resvg/resvg-js-android-arm64": "2.6.2", "@resvg/resvg-js-darwin-arm64": "2.6.2", "@resvg/resvg-js-darwin-x64": "2.6.2", "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2", "@resvg/resvg-js-linux-arm64-gnu": "2.6.2", "@resvg/resvg-js-linux-arm64-musl": "2.6.2", "@resvg/resvg-js-linux-x64-gnu": "2.6.2", "@resvg/resvg-js-linux-x64-musl": "2.6.2", "@resvg/resvg-js-win32-arm64-msvc": "2.6.2", "@resvg/resvg-js-win32-ia32-msvc": "2.6.2", "@resvg/resvg-js-win32-x64-msvc": "2.6.2" } }, "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q=="],
"@resvg/resvg-js-android-arm-eabi": ["@resvg/resvg-js-android-arm-eabi@2.6.2", "", { "os": "android", "cpu": "arm" }, "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA=="],
@@ -1729,6 +1767,8 @@
"are-docs-informative": ["are-docs-informative@0.0.2", "", {}, "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig=="],
+ "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
+
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
@@ -1803,6 +1843,8 @@
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.7", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-1ghYO3HnxGec0TCGBXiDLVns4eCSx4zJpxnHrlqFQajmhfKMQBzUGDdkMK7fUW7PTHTeLf+j87aTuKuuwWzMGw=="],
+ "basic-auth": ["basic-auth@2.0.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg=="],
+
"better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "^1.0.0" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="],
"better-sqlite3": ["better-sqlite3@12.8.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ=="],
@@ -1935,6 +1977,10 @@
"compress-commons": ["compress-commons@6.0.2", "", { "dependencies": { "crc-32": "^1.2.0", "crc32-stream": "^6.0.0", "is-stream": "^2.0.1", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg=="],
+ "compressible": ["compressible@2.0.18", "", { "dependencies": { "mime-db": ">= 1.43.0 < 2" } }, "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg=="],
+
+ "compression": ["compression@1.8.1", "", { "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" } }, "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w=="],
+
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"concat-stream": ["concat-stream@2.0.0", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="],
@@ -2023,6 +2069,8 @@
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
+ "dedent": ["dedent@1.7.2", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA=="],
+
"deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
@@ -2277,6 +2325,8 @@
"evlog-playground": ["evlog-playground@workspace:apps/playground"],
+ "evlog-react-router-example": ["evlog-react-router-example@workspace:examples/react-router"],
+
"evlog-solidstart-example": ["evlog-solidstart-example@workspace:examples/solidstart"],
"evlog-sveltekit-example": ["evlog-sveltekit-example@workspace:examples/sveltekit"],
@@ -2419,6 +2469,8 @@
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
+ "get-port": ["get-port@5.1.1", "", {}, "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ=="],
+
"get-port-please": ["get-port-please@3.2.0", "", {}, "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
@@ -2667,7 +2719,7 @@
"jsdoc-type-pratt-parser": ["jsdoc-type-pratt-parser@4.1.0", "", {}, "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg=="],
- "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
+ "jsesc": ["jsesc@3.0.2", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g=="],
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
@@ -2939,6 +2991,8 @@
"mocked-exports": ["mocked-exports@0.1.1", "", {}, "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA=="],
+ "morgan": ["morgan@1.10.1", "", { "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", "on-headers": "~1.1.0" } }, "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A=="],
+
"motion-dom": ["motion-dom@12.36.0", "", { "dependencies": { "motion-utils": "^12.36.0" } }, "sha512-Ep1pq8P88rGJ75om8lTCA13zqd7ywPGwCqwuWwin6BKc0hMLkVfcS6qKlRqEo2+t0DwoUcgGJfXwaiFn4AOcQA=="],
"motion-utils": ["motion-utils@12.36.0", "", {}, "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg=="],
@@ -3041,6 +3095,8 @@
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
+ "on-headers": ["on-headers@1.1.0", "", {}, "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A=="],
+
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
"one-time": ["one-time@1.0.0", "", { "dependencies": { "fn.name": "1.x.x" } }, "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g=="],
@@ -3075,7 +3131,7 @@
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
- "p-map": ["p-map@2.1.0", "", {}, "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="],
+ "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="],
"p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
@@ -3217,7 +3273,7 @@
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
- "prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="],
+ "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
"pretty-bytes": ["pretty-bytes@7.1.0", "", {}, "sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw=="],
@@ -3307,7 +3363,9 @@
"react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
- "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
+ "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
+
+ "react-router": ["react-router@7.13.1", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA=="],
"read-yaml-file": ["read-yaml-file@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.6.1", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA=="],
@@ -3443,7 +3501,7 @@
"serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
- "set-cookie-parser": ["set-cookie-parser@3.0.1", "", {}, "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q=="],
+ "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
@@ -3813,6 +3871,8 @@
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
+ "valibot": ["valibot@1.3.1", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-sfdRir/QFM0JaF22hqTroPc5xy4DimuGQVKFrzF1YfGwaS1nJot3Y8VqMdLO2Lg27fMzat2yD3pY5PbAYO39Gg=="],
+
"validate-npm-package-name": ["validate-npm-package-name@6.0.2", "", {}, "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ=="],
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
@@ -3975,10 +4035,10 @@
"@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
- "@babel/core/@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
-
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+ "@babel/generator/jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
+
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
"@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
@@ -3987,11 +4047,11 @@
"@babel/helper-module-transforms/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
- "@babel/template/@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
+ "@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
- "@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
+ "@changesets/apply-release-plan/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="],
- "@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
+ "@changesets/write/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="],
"@cloudflare/unenv-preset/unenv": ["unenv@2.0.0-rc.14", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.1", "ohash": "^2.0.10", "pathe": "^2.0.3", "ufo": "^1.5.4" } }, "sha512-od496pShMen7nOy5VmVJCnq8rptd45vh6Nx/r2iPbrba6pa6p+tS2ywuIHRZ/OBvSbQZB0kWvpO9XBNVFXHD3Q=="],
@@ -4145,6 +4205,16 @@
"@quansync/fs/quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="],
+ "@react-router/dev/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
+
+ "@react-router/dev/es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
+
+ "@react-router/dev/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
+
+ "@react-router/dev/vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
+
+ "@react-router/serve/express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="],
+
"@rollup/plugin-commonjs/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@rollup/plugin-commonjs/is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="],
@@ -4165,6 +4235,8 @@
"@sveltejs/kit/cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
+ "@sveltejs/kit/set-cookie-parser": ["set-cookie-parser@3.0.1", "", {}, "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q=="],
+
"@sveltejs/vite-plugin-svelte/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
"@sveltejs/vite-plugin-svelte-inspector/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
@@ -4191,9 +4263,9 @@
"@tanstack/devtools-vite/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
- "@tanstack/router-core/cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="],
+ "@tanstack/directive-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
- "@tanstack/router-generator/prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
+ "@tanstack/router-core/cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="],
"@tanstack/router-generator/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
@@ -4203,6 +4275,8 @@
"@tanstack/router-plugin/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
+ "@tanstack/server-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
+
"@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
"@tanstack/start-plugin-core/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.40", "", {}, "sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w=="],
@@ -4235,12 +4309,12 @@
"@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="],
+ "@vitejs/plugin-react/react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
+
"@vitejs/plugin-vue/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.2", "", {}, "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw=="],
"@vue-macros/common/ast-kit": ["ast-kit@2.2.0", "", { "dependencies": { "@babel/parser": "^7.28.5", "pathe": "^2.0.3" } }, "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw=="],
- "@vue/babel-plugin-resolve-type/@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
-
"@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
@@ -4267,6 +4341,8 @@
"babel-plugin-jsx-dom-expressions/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
+ "basic-auth/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
+
"bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"boxen/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
@@ -4293,6 +4369,10 @@
"compress-commons/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
+ "compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
+
+ "compression/negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="],
+
"crc32-stream/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
"cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
@@ -4327,6 +4407,8 @@
"evlog-nestjs-example/@nestjs/common": ["@nestjs/common@10.4.22", "", { "dependencies": { "file-type": "20.4.1", "iterare": "1.2.1", "tslib": "2.8.1", "uid": "2.0.2" }, "peerDependencies": { "class-transformer": "*", "class-validator": "*", "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, "optionalPeers": ["class-transformer", "class-validator"] }, "sha512-fxJ4v85nDHaqT1PmfNCQ37b/jcv2OojtXTaK1P2uAXhzLf9qq6WNUOFvxBrV4fhQek1EQoT1o9oj5xAZmv3NRw=="],
+ "evlog-react-router-example/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
+
"evlog-sveltekit-example/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
"evlog-tanstack-start-example/@types/node": ["@types/node@22.19.15", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg=="],
@@ -4381,8 +4463,6 @@
"isomorphic-git/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
- "json-schema-to-typescript/prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
-
"jsonc-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"jsonc-eslint-parser/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="],
@@ -4393,8 +4473,6 @@
"light-my-request/process-warning": ["process-warning@4.0.1", "", {}, "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q=="],
- "light-my-request/set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
-
"linebreak/base64-js": ["base64-js@0.0.8", "", {}, "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="],
"listhen/citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
@@ -4425,6 +4503,10 @@
"mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
+ "morgan/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
+
+ "morgan/on-finished": ["on-finished@2.3.0", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww=="],
+
"multer/type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
@@ -4477,6 +4559,8 @@
"oxc-parser/@oxc-project/types": ["@oxc-project/types@0.117.0", "", {}, "sha512-C/kPXBphID44fXdsa2xSOCuzX8fKZiFxPsvucJ6Yfkr6CJlMA+kNLPNKyLoI+l9XlDsNxBrz6h7IIjKU8pB69w=="],
+ "p-filter/p-map": ["p-map@2.1.0", "", {}, "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="],
+
"parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
"parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
@@ -4513,8 +4597,6 @@
"rolldown-plugin-dts/@babel/types": ["@babel/types@8.0.0-rc.2", "", { "dependencies": { "@babel/helper-string-parser": "^8.0.0-rc.2", "@babel/helper-validator-identifier": "^8.0.0-rc.2" } }, "sha512-91gAaWRznDwSX4E2tZ1YjBuIfnQVOFDCQ2r0Toby0gu4XEbyF623kXLMA8d4ZbCu+fINcrudkmEcwSUHgDDkNw=="],
- "rollup-plugin-dts/@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
-
"rollup-plugin-inject/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="],
"rollup-plugin-inject/magic-string": ["magic-string@0.25.9", "", { "dependencies": { "sourcemap-codec": "^1.4.8" } }, "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ=="],
@@ -4617,8 +4699,6 @@
"vite-node/vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
- "vite-plugin-checker/@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
-
"vite-plugin-checker/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"vite-plugin-inspect/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
@@ -4663,20 +4743,8 @@
"zip-stream/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
- "@babel/core/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
-
- "@babel/core/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
-
"@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
- "@babel/template/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
-
- "@babel/template/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
-
- "@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
-
- "@babel/traverse/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
-
"@codspeed/core/find-up/locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="],
"@codspeed/core/find-up/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
@@ -4923,6 +4991,38 @@
"@nuxtjs/i18n/vue-router/@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="],
+ "@react-router/dev/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
+
+ "@react-router/dev/vite/esbuild": ["esbuild@0.27.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.4", "@esbuild/android-arm": "0.27.4", "@esbuild/android-arm64": "0.27.4", "@esbuild/android-x64": "0.27.4", "@esbuild/darwin-arm64": "0.27.4", "@esbuild/darwin-x64": "0.27.4", "@esbuild/freebsd-arm64": "0.27.4", "@esbuild/freebsd-x64": "0.27.4", "@esbuild/linux-arm": "0.27.4", "@esbuild/linux-arm64": "0.27.4", "@esbuild/linux-ia32": "0.27.4", "@esbuild/linux-loong64": "0.27.4", "@esbuild/linux-mips64el": "0.27.4", "@esbuild/linux-ppc64": "0.27.4", "@esbuild/linux-riscv64": "0.27.4", "@esbuild/linux-s390x": "0.27.4", "@esbuild/linux-x64": "0.27.4", "@esbuild/netbsd-arm64": "0.27.4", "@esbuild/netbsd-x64": "0.27.4", "@esbuild/openbsd-arm64": "0.27.4", "@esbuild/openbsd-x64": "0.27.4", "@esbuild/openharmony-arm64": "0.27.4", "@esbuild/sunos-x64": "0.27.4", "@esbuild/win32-arm64": "0.27.4", "@esbuild/win32-ia32": "0.27.4", "@esbuild/win32-x64": "0.27.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ=="],
+
+ "@react-router/serve/express/accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
+
+ "@react-router/serve/express/body-parser": ["body-parser@1.20.4", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA=="],
+
+ "@react-router/serve/express/content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="],
+
+ "@react-router/serve/express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
+
+ "@react-router/serve/express/cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="],
+
+ "@react-router/serve/express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
+
+ "@react-router/serve/express/finalhandler": ["finalhandler@1.3.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="],
+
+ "@react-router/serve/express/fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="],
+
+ "@react-router/serve/express/merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="],
+
+ "@react-router/serve/express/path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="],
+
+ "@react-router/serve/express/qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="],
+
+ "@react-router/serve/express/send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="],
+
+ "@react-router/serve/express/serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="],
+
+ "@react-router/serve/express/type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
+
"@so-ric/colorspace/color/color-convert": ["color-convert@3.1.3", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg=="],
"@so-ric/colorspace/color/color-string": ["color-string@2.1.4", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg=="],
@@ -4963,10 +5063,18 @@
"@tanstack/devtools/@tanstack/devtools-client/@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.3.5", "", {}, "sha512-RL1f5ZlfZMpghrCIdzl6mLOFLTuhqmPNblZgBaeKfdtk5rfbjykurv+VfYydOFXj0vxVIoA2d/zT7xfD7Ph8fw=="],
+ "@tanstack/directive-functions-plugin/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
+
+ "@tanstack/directive-functions-plugin/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
+
"@tanstack/router-plugin/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"@tanstack/router-plugin/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
+ "@tanstack/server-functions-plugin/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
+
+ "@tanstack/server-functions-plugin/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
+
"@tanstack/start-plugin-core/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
"@tanstack/start-plugin-core/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
@@ -4975,10 +5083,6 @@
"@vinxi/listhen/@parcel/watcher-wasm/napi-wasm": ["napi-wasm@1.1.3", "", { "bundled": true }, "sha512-h/4nMGsHjZDCYmQVNODIrYACVJ+I9KItbG+0si6W/jSjdA9JbWDoU4LLeMXVcEQGHjttI2tuXqDrbGF7qkUHHg=="],
- "@vue/babel-plugin-resolve-type/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
-
- "@vue/babel-plugin-resolve-type/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
-
"ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"archiver-utils/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
@@ -5007,6 +5111,8 @@
"clipboardy/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
+ "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
+
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
@@ -5119,6 +5225,8 @@
"mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
+ "morgan/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
+
"multer/type-is/media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
"multer/type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
@@ -5185,11 +5293,9 @@
"readdir-glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
- "rolldown-plugin-dts/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@8.0.0-rc.2", "", {}, "sha512-noLx87RwlBEMrTzncWd/FvTxoJ9+ycHNg0n8yyYydIoDsLZuxknKgWRJUqcrVkNrJ74uGyhWQzQaS3q8xfGAhQ=="],
-
- "rollup-plugin-dts/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
+ "rolldown-plugin-dts/@babel/generator/jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
- "rollup-plugin-dts/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
+ "rolldown-plugin-dts/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@8.0.0-rc.2", "", {}, "sha512-noLx87RwlBEMrTzncWd/FvTxoJ9+ycHNg0n8yyYydIoDsLZuxknKgWRJUqcrVkNrJ74uGyhWQzQaS3q8xfGAhQ=="],
"rollup-plugin-visualizer/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="],
@@ -5269,10 +5375,6 @@
"vite-node/vite/esbuild": ["esbuild@0.27.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.4", "@esbuild/android-arm": "0.27.4", "@esbuild/android-arm64": "0.27.4", "@esbuild/android-x64": "0.27.4", "@esbuild/darwin-arm64": "0.27.4", "@esbuild/darwin-x64": "0.27.4", "@esbuild/freebsd-arm64": "0.27.4", "@esbuild/freebsd-x64": "0.27.4", "@esbuild/linux-arm": "0.27.4", "@esbuild/linux-arm64": "0.27.4", "@esbuild/linux-ia32": "0.27.4", "@esbuild/linux-loong64": "0.27.4", "@esbuild/linux-mips64el": "0.27.4", "@esbuild/linux-ppc64": "0.27.4", "@esbuild/linux-riscv64": "0.27.4", "@esbuild/linux-s390x": "0.27.4", "@esbuild/linux-x64": "0.27.4", "@esbuild/netbsd-arm64": "0.27.4", "@esbuild/netbsd-x64": "0.27.4", "@esbuild/openbsd-arm64": "0.27.4", "@esbuild/openbsd-x64": "0.27.4", "@esbuild/openharmony-arm64": "0.27.4", "@esbuild/sunos-x64": "0.27.4", "@esbuild/win32-arm64": "0.27.4", "@esbuild/win32-ia32": "0.27.4", "@esbuild/win32-x64": "0.27.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ=="],
- "vite-plugin-checker/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
-
- "vite-plugin-checker/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
-
"vite-plugin-checker/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"vitest/vite/esbuild": ["esbuild@0.27.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.4", "@esbuild/android-arm": "0.27.4", "@esbuild/android-arm64": "0.27.4", "@esbuild/android-x64": "0.27.4", "@esbuild/darwin-arm64": "0.27.4", "@esbuild/darwin-x64": "0.27.4", "@esbuild/freebsd-arm64": "0.27.4", "@esbuild/freebsd-x64": "0.27.4", "@esbuild/linux-arm": "0.27.4", "@esbuild/linux-arm64": "0.27.4", "@esbuild/linux-ia32": "0.27.4", "@esbuild/linux-loong64": "0.27.4", "@esbuild/linux-mips64el": "0.27.4", "@esbuild/linux-ppc64": "0.27.4", "@esbuild/linux-riscv64": "0.27.4", "@esbuild/linux-s390x": "0.27.4", "@esbuild/linux-x64": "0.27.4", "@esbuild/netbsd-arm64": "0.27.4", "@esbuild/netbsd-x64": "0.27.4", "@esbuild/openbsd-arm64": "0.27.4", "@esbuild/openbsd-x64": "0.27.4", "@esbuild/openharmony-arm64": "0.27.4", "@esbuild/sunos-x64": "0.27.4", "@esbuild/win32-arm64": "0.27.4", "@esbuild/win32-ia32": "0.27.4", "@esbuild/win32-x64": "0.27.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ=="],
@@ -5499,6 +5601,74 @@
"@nuxthub/core/tsdown/rolldown-plugin-dts/ast-kit": ["ast-kit@2.2.0", "", { "dependencies": { "@babel/parser": "^7.28.5", "pathe": "^2.0.3" } }, "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw=="],
+ "@react-router/dev/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.4", "", { "os": "android", "cpu": "arm" }, "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.4", "", { "os": "android", "cpu": "arm64" }, "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.4", "", { "os": "android", "cpu": "x64" }, "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.4", "", { "os": "linux", "cpu": "arm" }, "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.4", "", { "os": "linux", "cpu": "x64" }, "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.4", "", { "os": "none", "cpu": "arm64" }, "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.4", "", { "os": "none", "cpu": "x64" }, "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.4", "", { "os": "none", "cpu": "arm64" }, "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw=="],
+
+ "@react-router/dev/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.4", "", { "os": "win32", "cpu": "x64" }, "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg=="],
+
+ "@react-router/serve/express/accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
+
+ "@react-router/serve/express/accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
+
+ "@react-router/serve/express/body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
+
+ "@react-router/serve/express/body-parser/raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="],
+
+ "@react-router/serve/express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
+
+ "@react-router/serve/express/send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
+
+ "@react-router/serve/express/type-is/media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
+
+ "@react-router/serve/express/type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
+
"@so-ric/colorspace/color/color-convert/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="],
"@so-ric/colorspace/color/color-string/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="],
@@ -5757,6 +5927,10 @@
"@nuxt/test-utils/@nuxt/devtools-kit/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
+ "@react-router/serve/express/accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
+
+ "@react-router/serve/express/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
+
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="],
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="],
diff --git a/examples/react-router/.gitignore b/examples/react-router/.gitignore
new file mode 100644
index 00000000..31d2101d
--- /dev/null
+++ b/examples/react-router/.gitignore
@@ -0,0 +1 @@
+.react-router
diff --git a/examples/react-router/README.md b/examples/react-router/README.md
new file mode 100644
index 00000000..70616414
--- /dev/null
+++ b/examples/react-router/README.md
@@ -0,0 +1,9 @@
+# React Router Example
+
+```bash
+bun run dev
+```
+
+Open [http://localhost:5173](http://localhost:5173) to test routes from the UI.
+
+Check your terminal for the pretty-printed wide events and your PostHog dashboard for drained logs.
diff --git a/examples/react-router/app/root.tsx b/examples/react-router/app/root.tsx
new file mode 100644
index 00000000..c48be32a
--- /dev/null
+++ b/examples/react-router/app/root.tsx
@@ -0,0 +1,60 @@
+import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'
+import { initLogger, parseError, type EnrichContext } from 'evlog'
+import { evlog } from 'evlog/react-router'
+import { createPostHogDrain } from 'evlog/posthog'
+import type { Route } from './+types/root'
+
+initLogger({
+ env: { service: 'react-router-example' },
+ pretty: true,
+})
+
+export const middleware: Route.MiddlewareFunction[] = [
+ evlog({
+ exclude: ['/'],
+ drain: createPostHogDrain(),
+ enrich: (ctx: EnrichContext) => {
+ ctx.event.runtime = 'node'
+ ctx.event.pid = process.pid
+ },
+ }),
+]
+
+export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
+ const parsed = parseError(error)
+ return (
+
+
+
+
+
+
+
+ Error {parsed.status}
+ {parsed.message}
+ {parsed.why && Why: {parsed.why}
}
+ {parsed.fix && Fix: {parsed.fix}
}
+ {parsed.link && Learn more
}
+
+
+
+ )
+}
+
+export default function Root() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/examples/react-router/app/routes.ts b/examples/react-router/app/routes.ts
new file mode 100644
index 00000000..5d30b231
--- /dev/null
+++ b/examples/react-router/app/routes.ts
@@ -0,0 +1,8 @@
+import { type RouteConfig, index, route } from '@react-router/dev/routes'
+
+export default [
+ index('routes/home.tsx'),
+ route('health', 'routes/health.tsx'),
+ route('users/:id', 'routes/users.$id.tsx'),
+ route('checkout', 'routes/checkout.tsx'),
+] satisfies RouteConfig
diff --git a/examples/react-router/app/routes/checkout.tsx b/examples/react-router/app/routes/checkout.tsx
new file mode 100644
index 00000000..e9c383ce
--- /dev/null
+++ b/examples/react-router/app/routes/checkout.tsx
@@ -0,0 +1,16 @@
+import { loggerContext } from 'evlog/react-router'
+import { createError } from 'evlog'
+import type { Route } from './+types/checkout'
+
+export async function loader({ context }: Route.LoaderArgs) {
+ const log = context.get(loggerContext)
+ log.set({ cart: { items: 3, total: 9999 } })
+
+ throw createError({
+ message: 'Payment failed',
+ status: 402,
+ why: 'Card declined by issuer',
+ fix: 'Try a different card or payment method',
+ link: 'https://docs.example.com/payments/declined',
+ })
+}
diff --git a/examples/react-router/app/routes/health.tsx b/examples/react-router/app/routes/health.tsx
new file mode 100644
index 00000000..170cdcf7
--- /dev/null
+++ b/examples/react-router/app/routes/health.tsx
@@ -0,0 +1,8 @@
+import { loggerContext } from 'evlog/react-router'
+import type { Route } from './+types/health'
+
+export async function loader({ context }: Route.LoaderArgs) {
+ const log = context.get(loggerContext)
+ log.set({ route: 'health' })
+ return Response.json({ ok: true })
+}
diff --git a/examples/react-router/app/routes/home.tsx b/examples/react-router/app/routes/home.tsx
new file mode 100644
index 00000000..8a2f4e09
--- /dev/null
+++ b/examples/react-router/app/routes/home.tsx
@@ -0,0 +1,7 @@
+import { testUI } from '../ui'
+
+export function loader() {
+ return new Response(testUI(), {
+ headers: { 'Content-Type': 'text/html' },
+ })
+}
diff --git a/examples/react-router/app/routes/users.$id.tsx b/examples/react-router/app/routes/users.$id.tsx
new file mode 100644
index 00000000..e7095f6b
--- /dev/null
+++ b/examples/react-router/app/routes/users.$id.tsx
@@ -0,0 +1,24 @@
+import { loggerContext, useLogger } from 'evlog/react-router'
+import type { Route } from './+types/users.$id'
+
+async function fetchUserOrders(userId: string) {
+ const log = useLogger()
+ log.set({ db: { query: 'fetchUserOrders', userId } })
+ return [{ id: 'order_1', total: 4999 }, { id: 'order_2', total: 1299 }]
+}
+
+export async function loader({ params, context }: Route.LoaderArgs) {
+ const log = context.get(loggerContext)
+ const userId = params.id
+
+ log.set({ user: { id: userId } })
+ const user = { id: userId, name: 'Alice', plan: 'pro', email: 'alice@example.com' }
+
+ const [local, domain] = user.email.split('@')
+ log.set({ user: { name: user.name, plan: user.plan, email: `${local[0]}***@${domain}` } })
+
+ const orders = await fetchUserOrders(userId)
+ log.set({ orders: { count: orders.length, totalRevenue: orders.reduce((sum, o) => sum + o.total, 0) } })
+
+ return Response.json({ user, orders })
+}
diff --git a/examples/react-router/app/ui.ts b/examples/react-router/app/ui.ts
new file mode 100644
index 00000000..e10d081c
--- /dev/null
+++ b/examples/react-router/app/ui.ts
@@ -0,0 +1,254 @@
+interface Route {
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
+ path: string
+ description: string
+ body?: string
+}
+
+const routes: Route[] = [
+ { method: 'GET', path: '/health', description: 'Health check with basic log.set()' },
+ { method: 'GET', path: '/users/42', description: 'User lookup with context accumulation + email masking' },
+ { method: 'GET', path: '/checkout', description: 'Throws createError() with status/why/fix/link' },
+]
+
+export function testUI(): string {
+ const routeButtons = routes.map(r => `
+
+ `).join('')
+
+ return `
+
+
+
+
+ evlog — React Router Example
+
+
+
+
+
+ evlog
+ react-router-example
+
+
+
Routes
+
${routeButtons}
+
+
Response
+
+
+
Click a route to test
+
+
+
+
+
+`
+}
diff --git a/examples/react-router/package.json b/examples/react-router/package.json
new file mode 100644
index 00000000..69a63c53
--- /dev/null
+++ b/examples/react-router/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "evlog-react-router-example",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "react-router dev",
+ "build": "react-router build",
+ "start": "react-router-serve ./build/server/index.js"
+ },
+ "dependencies": {
+ "@react-router/node": "^7.9.0",
+ "@react-router/serve": "^7.9.0",
+ "evlog": "workspace:*",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-router": "^7.9.0",
+ "isbot": "^5"
+ },
+ "devDependencies": {
+ "@react-router/dev": "^7.9.0",
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0",
+ "typescript": "^5.9.3",
+ "vite": "^6.0.0"
+ }
+}
diff --git a/examples/react-router/react-router.config.ts b/examples/react-router/react-router.config.ts
new file mode 100644
index 00000000..254f6b49
--- /dev/null
+++ b/examples/react-router/react-router.config.ts
@@ -0,0 +1,7 @@
+import type { Config } from '@react-router/dev/config'
+
+export default {
+ future: {
+ v8_middleware: true,
+ },
+} satisfies Config
diff --git a/examples/react-router/tsconfig.json b/examples/react-router/tsconfig.json
new file mode 100644
index 00000000..ef6f7aea
--- /dev/null
+++ b/examples/react-router/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "include": [
+ "**/*.ts",
+ "**/*.tsx",
+ ".react-router/types/**/*"
+ ],
+ "compilerOptions": {
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
+ "target": "ES2022",
+ "module": "ES2022",
+ "moduleResolution": "bundler",
+ "jsx": "react-jsx",
+ "strict": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "isolatedModules": true,
+ "rootDirs": [".", ".react-router/types"],
+ "paths": {
+ "~/*": ["./app/*"]
+ },
+ "types": ["vite/client"]
+ }
+}
diff --git a/examples/react-router/vite.config.ts b/examples/react-router/vite.config.ts
new file mode 100644
index 00000000..747f8218
--- /dev/null
+++ b/examples/react-router/vite.config.ts
@@ -0,0 +1,6 @@
+import { reactRouter } from '@react-router/dev/vite'
+import { defineConfig } from 'vite'
+
+export default defineConfig({
+ plugins: [reactRouter()],
+})
diff --git a/package.json b/package.json
index 54d0c777..d3e85a8c 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"example:fastify": "dotenv -- turbo run dev --filter=evlog-fastify-example",
"example:nestjs": "dotenv -- turbo run dev --filter=evlog-nestjs-example",
"example:sveltekit": "dotenv -- turbo run dev --filter=evlog-sveltekit-example",
+ "example:react-router": "dotenv -- turbo run dev --filter=evlog-react-router-example",
"example:hono": "dotenv -- turbo run dev --filter=evlog-hono-example",
"example:workers": "dotenv -- turbo run dev --filter=evlog-workers-example",
"example:browser": "dotenv -- turbo run dev --filter=evlog-browser-example",
diff --git a/packages/evlog/README.md b/packages/evlog/README.md
index 68211347..d1fdd124 100644
--- a/packages/evlog/README.md
+++ b/packages/evlog/README.md
@@ -464,6 +464,33 @@ Use `useLogger()` to access the logger from anywhere in the call stack.
See the full [elysia example](https://github.com/HugoRCD/evlog/tree/main/examples/elysia) for a complete working project.
+## React Router
+
+```typescript
+// app/root.tsx
+import { initLogger } from 'evlog'
+import { evlog, loggerContext } from 'evlog/react-router'
+
+initLogger({ env: { service: 'react-router-api' } })
+
+export const middleware: Route.MiddlewareFunction[] = [
+ evlog(),
+]
+
+// app/routes/api.users.$id.tsx
+import { loggerContext } from 'evlog/react-router'
+
+export async function loader({ params, context }: Route.LoaderArgs) {
+ const log = context.get(loggerContext)
+ log.set({ users: { count: 42 } })
+ return { users: [] }
+}
+```
+
+Use `context.get(loggerContext)` in loaders/actions, or `useLogger()` from anywhere in the call stack. Requires `v8_middleware: true` in `react-router.config.ts`.
+
+See the full [react-router example](https://github.com/HugoRCD/evlog/tree/main/examples/react-router) for a complete working project.
+
## NestJS
```typescript
@@ -1095,6 +1122,7 @@ try {
| **Nitro v3** | `modules: [evlog()]` with `import evlog from 'evlog/nitro/v3'` |
| **Nitro v2** | `modules: [evlog()]` with `import evlog from 'evlog/nitro'` |
| **TanStack Start** | Nitro v3 module setup ([example](./examples/tanstack-start)) |
+| **React Router** | `evlog()` middleware with `import { evlog } from 'evlog/react-router'` ([example](./examples/react-router)) |
| **NestJS** | `EvlogModule.forRoot()` with `import { EvlogModule } from 'evlog/nestjs'` ([example](./examples/nestjs)) |
| **Express** | `app.use(evlog())` with `import { evlog } from 'evlog/express'` ([example](./examples/express)) |
| **Hono** | `app.use(evlog())` with `import { evlog } from 'evlog/hono'` ([example](./examples/hono)) |
diff --git a/packages/evlog/package.json b/packages/evlog/package.json
index 244cb4bb..0a288110 100644
--- a/packages/evlog/package.json
+++ b/packages/evlog/package.json
@@ -25,6 +25,7 @@
"elysia",
"nestjs",
"sveltekit",
+ "react-router",
"vite",
"typescript"
],
@@ -137,6 +138,11 @@
"import": "./dist/nestjs/index.mjs",
"default": "./dist/nestjs/index.mjs"
},
+ "./react-router": {
+ "types": "./dist/react-router/index.d.mts",
+ "import": "./dist/react-router/index.mjs",
+ "default": "./dist/react-router/index.mjs"
+ },
"./sveltekit": {
"types": "./dist/sveltekit/index.d.mts",
"import": "./dist/sveltekit/index.mjs",
@@ -230,6 +236,9 @@
"nestjs": [
"./dist/nestjs/index.d.mts"
],
+ "react-router": [
+ "./dist/react-router/index.d.mts"
+ ],
"sveltekit": [
"./dist/sveltekit/index.d.mts"
],
@@ -287,6 +296,7 @@
"nitropack": "^2.13.1",
"nuxt": "^4.4.2",
"pino": "^10.3.1",
+ "react-router": "^7.9.0",
"supertest": "^7.2.2",
"tsdown": "^0.21.4",
"magic-string": "^0.30.21",
@@ -308,6 +318,7 @@
"elysia": ">=1.4.27",
"fastify": ">=5.8.2",
"@nestjs/common": ">=11.1.17",
+ "react-router": ">=7.9.0",
"vite": "^7.0.0 || ^8.0.0",
"ai": ">=6.0.116"
},
@@ -348,6 +359,9 @@
"@nestjs/common": {
"optional": true
},
+ "react-router": {
+ "optional": true
+ },
"vite": {
"optional": true
},
diff --git a/packages/evlog/src/react-router/index.ts b/packages/evlog/src/react-router/index.ts
new file mode 100644
index 00000000..c0ea773c
--- /dev/null
+++ b/packages/evlog/src/react-router/index.ts
@@ -0,0 +1,73 @@
+import { createContext } from 'react-router'
+import type { RequestLogger } from '../types'
+import { createMiddlewareLogger, type BaseEvlogOptions } from '../shared/middleware'
+import { extractSafeHeaders } from '../shared/headers'
+import { createLoggerStorage } from '../shared/storage'
+
+const { storage, useLogger } = createLoggerStorage(
+ 'middleware context. Make sure the evlog middleware is added to your route.',
+)
+
+/**
+ * Typed context key for accessing the evlog logger in loaders and actions.
+ *
+ * @example
+ * ```ts
+ * import { loggerContext } from 'evlog/react-router'
+ *
+ * export async function loader({ context }: Route.LoaderArgs) {
+ * const log = context.get(loggerContext)
+ * log.set({ user: { id: 'u-1' } })
+ * return { ok: true }
+ * }
+ * ```
+ */
+export const loggerContext = createContext()
+
+export type EvlogReactRouterOptions = BaseEvlogOptions
+
+export { useLogger }
+
+/**
+ * Create an evlog middleware for React Router.
+ *
+ * @example
+ * ```ts
+ * // app/root.tsx
+ * import { evlog } from 'evlog/react-router'
+ *
+ * export const middleware: Route.MiddlewareFunction[] = [
+ * evlog({ drain: createAxiomDrain() }),
+ * ]
+ * ```
+ */
+export function evlog(options: EvlogReactRouterOptions = {}) {
+ return async (
+ { request, context }: { request: Request; context: { set(ctx: unknown, value: unknown): void } },
+ next: () => Promise,
+ ): Promise => {
+ const url = new URL(request.url)
+ const { logger, finish, skipped } = createMiddlewareLogger({
+ method: request.method,
+ path: url.pathname,
+ requestId: request.headers.get('x-request-id') || crypto.randomUUID(),
+ headers: extractSafeHeaders(request.headers),
+ ...options,
+ })
+
+ if (skipped) {
+ return next()
+ }
+
+ context.set(loggerContext, logger)
+
+ try {
+ const response = await storage.run(logger, () => next())
+ await finish({ status: response.status })
+ return response
+ } catch (error) {
+ await finish({ error: error as Error })
+ throw error
+ }
+ }
+}
diff --git a/packages/evlog/test/react-router.test.ts b/packages/evlog/test/react-router.test.ts
new file mode 100644
index 00000000..290248a9
--- /dev/null
+++ b/packages/evlog/test/react-router.test.ts
@@ -0,0 +1,416 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+import { initLogger } from '../src/logger'
+import { evlog, loggerContext, useLogger } from '../src/react-router/index'
+import {
+ assertDrainCalledWith,
+ assertEnrichBeforeDrain,
+ assertSensitiveHeadersFiltered,
+ createPipelineSpies,
+} from './helpers/framework'
+
+function createMockContext() {
+ const store = new Map()
+ return {
+ set: (key: unknown, value: unknown) => store.set(key, value),
+ get: (key: unknown) => store.get(key),
+ }
+}
+
+function createRequest(path: string, init?: RequestInit) {
+ return new Request(`http://localhost${path}`, init)
+}
+
+function okResponse() {
+ return Promise.resolve(new Response('ok', { status: 200 }))
+}
+
+describe('evlog/react-router', () => {
+ beforeEach(() => {
+ initLogger({
+ env: { service: 'react-router-test' },
+ pretty: false,
+ })
+ vi.spyOn(console, 'log').mockImplementation(() => {})
+ vi.spyOn(console, 'info').mockImplementation(() => {})
+ vi.spyOn(console, 'error').mockImplementation(() => {})
+ vi.spyOn(console, 'warn').mockImplementation(() => {})
+ })
+
+ afterEach(() => {
+ vi.restoreAllMocks()
+ })
+
+ it('creates a logger accessible via context.get(loggerContext)', async () => {
+ const middleware = evlog()
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ await middleware({ request: createRequest('/api/test'), context }, next)
+
+ const logger = context.get(loggerContext)
+ expect(logger).toBeDefined()
+ expect(typeof logger.set).toBe('function')
+ })
+
+ it('emits event with correct method, path, and status', async () => {
+ const middleware = evlog()
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ const consoleSpy = vi.mocked(console.info)
+ await middleware({ request: createRequest('/api/users'), context }, next)
+
+ const lastCall = consoleSpy.mock.calls.find(call =>
+ typeof call[0] === 'string' && call[0].includes('"path":"/api/users"'),
+ )
+ expect(lastCall).toBeDefined()
+
+ const event = JSON.parse(lastCall![0] as string)
+ expect(event.method).toBe('GET')
+ expect(event.path).toBe('/api/users')
+ expect(event.status).toBe(200)
+ expect(event.level).toBe('info')
+ expect(event.duration).toBeDefined()
+ })
+
+ it('accumulates context set by loader', async () => {
+ const middleware = evlog()
+ const context = createMockContext()
+ const next = vi.fn(() => {
+ const logger = context.get(loggerContext) as any
+ logger.set({ user: { id: 'u-1' }, db: { queries: 3 } })
+ return okResponse()
+ })
+
+ const consoleSpy = vi.mocked(console.info)
+ await middleware({ request: createRequest('/api/users'), context }, next)
+
+ const lastCall = consoleSpy.mock.calls.find(call =>
+ typeof call[0] === 'string' && call[0].includes('"user"'),
+ )
+ expect(lastCall).toBeDefined()
+
+ const event = JSON.parse(lastCall![0] as string)
+ expect(event.user.id).toBe('u-1')
+ expect(event.db.queries).toBe(3)
+ })
+
+ it('logs status 500 when handler throws', async () => {
+ const middleware = evlog()
+ const context = createMockContext()
+ const next = vi.fn(() => Promise.reject(new Error('Something broke')))
+
+ const errorSpy = vi.mocked(console.error)
+ await expect(
+ middleware({ request: createRequest('/api/fail'), context }, next),
+ ).rejects.toThrow('Something broke')
+
+ const lastCall = errorSpy.mock.calls.find(call =>
+ typeof call[0] === 'string' && call[0].includes('"path":"/api/fail"'),
+ )
+ expect(lastCall).toBeDefined()
+
+ const event = JSON.parse(lastCall![0] as string)
+ expect(event.status).toBe(500)
+ expect(event.path).toBe('/api/fail')
+ })
+
+ it('re-throws all errors from handler', async () => {
+ const middleware = evlog()
+ const context = createMockContext()
+ const next = vi.fn(() => Promise.reject(new TypeError('unexpected')))
+
+ await expect(
+ middleware({ request: createRequest('/api/fail'), context }, next),
+ ).rejects.toThrow('unexpected')
+ })
+
+ it('skips routes not matching include patterns', async () => {
+ const middleware = evlog({ include: ['/api/**'] })
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ await middleware({ request: createRequest('/health'), context }, next)
+
+ const logger = context.get(loggerContext)
+ expect(logger).toBeUndefined()
+ })
+
+ it('logs routes matching include patterns', async () => {
+ const middleware = evlog({ include: ['/api/**'] })
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ const consoleSpy = vi.mocked(console.info)
+ await middleware({ request: createRequest('/api/data'), context }, next)
+
+ const lastCall = consoleSpy.mock.calls.find(call =>
+ typeof call[0] === 'string' && call[0].includes('"path":"/api/data"'),
+ )
+ expect(lastCall).toBeDefined()
+ })
+
+ it('uses x-request-id header when present', async () => {
+ const middleware = evlog()
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ const consoleSpy = vi.mocked(console.info)
+ await middleware({
+ request: createRequest('/api/test', {
+ headers: { 'x-request-id': 'custom-req-id' },
+ }),
+ context,
+ }, next)
+
+ const lastCall = consoleSpy.mock.calls.find(call =>
+ typeof call[0] === 'string' && call[0].includes('custom-req-id'),
+ )
+ expect(lastCall).toBeDefined()
+
+ const event = JSON.parse(lastCall![0] as string)
+ expect(event.requestId).toBe('custom-req-id')
+ })
+
+ it('handles POST requests with correct method', async () => {
+ const middleware = evlog()
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ const consoleSpy = vi.mocked(console.info)
+ await middleware({
+ request: createRequest('/api/checkout', { method: 'POST' }),
+ context,
+ }, next)
+
+ const lastCall = consoleSpy.mock.calls.find(call =>
+ typeof call[0] === 'string' && call[0].includes('"method":"POST"'),
+ )
+ expect(lastCall).toBeDefined()
+ })
+
+ it('excludes routes matching exclude patterns', async () => {
+ const middleware = evlog({ exclude: ['/_internal/**'] })
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ await middleware({ request: createRequest('/_internal/probe'), context }, next)
+
+ const logger = context.get(loggerContext)
+ expect(logger).toBeUndefined()
+ })
+
+ it('applies route-based service override', async () => {
+ const middleware = evlog({
+ routes: { '/api/auth/**': { service: 'auth-service' } },
+ })
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ const consoleSpy = vi.mocked(console.info)
+ await middleware({ request: createRequest('/api/auth/login'), context }, next)
+
+ const lastCall = consoleSpy.mock.calls.find(call =>
+ typeof call[0] === 'string' && call[0].includes('"service":"auth-service"'),
+ )
+ expect(lastCall).toBeDefined()
+ })
+
+ describe('drain / enrich / keep', () => {
+ it('calls drain with emitted event (shared helpers)', async () => {
+ const { drain } = createPipelineSpies()
+
+ const middleware = evlog({ drain })
+ const context = createMockContext()
+ const next = vi.fn(() => {
+ const logger = context.get(loggerContext) as any
+ logger.set({ user: { id: 'u-1' } })
+ return okResponse()
+ })
+
+ await middleware({ request: createRequest('/api/test'), context }, next)
+
+ assertDrainCalledWith(drain, { path: '/api/test', method: 'GET', level: 'info', status: 200 })
+ const [[ctx]] = drain.mock.calls
+ expect(ctx.headers).toBeDefined()
+ })
+
+ it('calls enrich before drain (shared helpers)', async () => {
+ const { drain, enrich } = createPipelineSpies()
+ enrich.mockImplementation((ctx) => {
+ ctx.event.enriched = true
+ })
+
+ const middleware = evlog({ enrich, drain })
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ await middleware({ request: createRequest('/api/test'), context }, next)
+
+ assertEnrichBeforeDrain(enrich, drain)
+ expect(drain.mock.calls[0][0].event.enriched).toBe(true)
+ })
+
+ it('enrich receives response status and safe headers', async () => {
+ const { enrich } = createPipelineSpies()
+
+ const middleware = evlog({ enrich })
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ await middleware({
+ request: createRequest('/api/test', {
+ headers: { 'user-agent': 'test-bot/1.0', 'x-custom': 'value' },
+ }),
+ context,
+ }, next)
+
+ expect(enrich).toHaveBeenCalledOnce()
+ const [[ctx]] = enrich.mock.calls
+ expect(ctx.response!.status).toBe(200)
+ expect(ctx.headers!['user-agent']).toBe('test-bot/1.0')
+ expect(ctx.headers!['x-custom']).toBe('value')
+ })
+
+ it('filters sensitive headers (shared helpers)', async () => {
+ const { drain } = createPipelineSpies()
+
+ const middleware = evlog({ drain })
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ await middleware({
+ request: createRequest('/api/test', {
+ headers: {
+ 'authorization': 'Bearer secret-token',
+ 'cookie': 'session=abc',
+ 'x-safe': 'visible',
+ },
+ }),
+ context,
+ }, next)
+
+ assertSensitiveHeadersFiltered(drain.mock.calls[0][0])
+ expect(drain.mock.calls[0][0].headers!['x-safe']).toBe('visible')
+ })
+
+ it('calls keep callback for tail sampling', async () => {
+ const { keep, drain } = createPipelineSpies()
+ keep.mockImplementation((ctx) => {
+ if (ctx.context.important) ctx.shouldKeep = true
+ })
+
+ const middleware = evlog({ keep, drain })
+ const context = createMockContext()
+ const next = vi.fn(() => {
+ const logger = context.get(loggerContext) as any
+ logger.set({ important: true })
+ return okResponse()
+ })
+
+ await middleware({ request: createRequest('/api/test'), context }, next)
+
+ expect(keep).toHaveBeenCalledOnce()
+ expect(keep.mock.calls[0][0].path).toBe('/api/test')
+ expect(drain).toHaveBeenCalledOnce()
+ })
+
+ it('calls drain on error responses', async () => {
+ const { drain } = createPipelineSpies()
+
+ const middleware = evlog({ drain })
+ const context = createMockContext()
+ const next = vi.fn(() => {
+ const logger = context.get(loggerContext) as any
+ logger.error(new Error('something broke'))
+ return Promise.resolve(new Response('error', { status: 500 }))
+ })
+
+ await middleware({ request: createRequest('/api/fail'), context }, next)
+
+ assertDrainCalledWith(drain, { path: '/api/fail', level: 'error', status: 500 })
+ })
+
+ it('drain error does not break request', async () => {
+ const drain = vi.fn(() => {
+ throw new Error('drain exploded')
+ })
+
+ const middleware = evlog({ drain })
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ const response = await middleware({ request: createRequest('/api/test'), context }, next)
+ expect(response.status).toBe(200)
+ expect(drain).toHaveBeenCalledOnce()
+ })
+
+ it('enrich error does not prevent drain', async () => {
+ const { drain } = createPipelineSpies()
+ const enrich = vi.fn(() => {
+ throw new Error('enrich exploded')
+ })
+
+ const middleware = evlog({ enrich, drain })
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ const response = await middleware({ request: createRequest('/api/test'), context }, next)
+ expect(response.status).toBe(200)
+ expect(enrich).toHaveBeenCalledOnce()
+ expect(drain).toHaveBeenCalledOnce()
+ })
+
+ it('does not call drain/enrich when route is skipped', async () => {
+ const { drain, enrich } = createPipelineSpies()
+
+ const middleware = evlog({ include: ['/api/**'], drain, enrich })
+ const context = createMockContext()
+ const next = vi.fn(() => okResponse())
+
+ await middleware({ request: createRequest('/health'), context }, next)
+
+ expect(drain).not.toHaveBeenCalled()
+ expect(enrich).not.toHaveBeenCalled()
+ })
+ })
+
+ describe('useLogger', () => {
+ it('returns same logger in middleware context', async () => {
+ let loggerFromUseLogger: any
+
+ const middleware = evlog()
+ const context = createMockContext()
+ const next = vi.fn(() => {
+ loggerFromUseLogger = useLogger()
+ return okResponse()
+ })
+
+ await middleware({ request: createRequest('/api/test'), context }, next)
+
+ const loggerFromContext = context.get(loggerContext)
+ expect(loggerFromUseLogger).toBe(loggerFromContext)
+ })
+
+ it('throws outside middleware context', () => {
+ expect(() => useLogger()).toThrow('[evlog] useLogger()')
+ })
+
+ it('works across async boundaries', async () => {
+ let loggerFromAsync: any
+
+ const middleware = evlog()
+ const context = createMockContext()
+ const next = vi.fn(async () => {
+ await new Promise(resolve => setTimeout(resolve, 1))
+ loggerFromAsync = useLogger()
+ return new Response('ok', { status: 200 })
+ })
+
+ await middleware({ request: createRequest('/api/test'), context }, next)
+
+ expect(loggerFromAsync).toBeDefined()
+ expect(typeof loggerFromAsync.set).toBe('function')
+ })
+ })
+})
diff --git a/packages/evlog/tsdown.config.ts b/packages/evlog/tsdown.config.ts
index c03a1aaf..defdc700 100644
--- a/packages/evlog/tsdown.config.ts
+++ b/packages/evlog/tsdown.config.ts
@@ -39,6 +39,7 @@ export default defineConfig({
'elysia/index': 'src/elysia/index.ts',
'fastify/index': 'src/fastify/index.ts',
'nestjs/index': 'src/nestjs/index.ts',
+ 'react-router/index': 'src/react-router/index.ts',
'sveltekit/index': 'src/sveltekit/index.ts',
'vite/index': 'src/vite/index.ts',
'client': 'src/client.ts',
@@ -76,6 +77,7 @@ export default defineConfig({
'fastify',
'@nestjs/common',
'@nestjs/core',
+ 'react-router',
'@sveltejs/kit',
'vite',
'ai',
diff --git a/skills/review-logging-patterns/SKILL.md b/skills/review-logging-patterns/SKILL.md
index 64794dfa..cdf8a487 100644
--- a/skills/review-logging-patterns/SKILL.md
+++ b/skills/review-logging-patterns/SKILL.md
@@ -1,6 +1,6 @@
---
name: review-logging-patterns
-description: Review code for logging patterns and suggest evlog adoption. Guides setup on Nuxt, Next.js, SvelteKit, Nitro, TanStack Start, NestJS, Express, Hono, Fastify, Elysia, Cloudflare Workers, and standalone TypeScript. Detects console.log spam, unstructured errors, and missing context. Covers wide events, structured errors, drain adapters (Axiom, OTLP, PostHog, Sentry, Better Stack), sampling, enrichers, and AI SDK integration (token usage, tool calls, streaming metrics).
+description: Review code for logging patterns and suggest evlog adoption. Guides setup on Nuxt, Next.js, SvelteKit, Nitro, TanStack Start, React Router, NestJS, Express, Hono, Fastify, Elysia, Cloudflare Workers, and standalone TypeScript. Detects console.log spam, unstructured errors, and missing context. Covers wide events, structured errors, drain adapters (Axiom, OTLP, PostHog, Sentry, Better Stack), sampling, enrichers, and AI SDK integration (token usage, tool calls, streaming metrics).
license: MIT
metadata:
author: HugoRCD
@@ -558,6 +558,72 @@ app.use(evlog({
}))
```
+### React Router
+
+```typescript
+// react-router.config.ts
+import type { Config } from '@react-router/dev/config'
+
+export default {
+ future: {
+ v8_middleware: true,
+ },
+} satisfies Config
+```
+
+```typescript
+// app/root.tsx
+import { initLogger } from 'evlog'
+import { evlog } from 'evlog/react-router'
+
+initLogger({ env: { service: 'my-api' } })
+
+export const middleware: Route.MiddlewareFunction[] = [
+ evlog(),
+]
+```
+
+Access the logger via `context.get(loggerContext)` in loaders and actions:
+
+```typescript
+// app/routes/api.users.$id.tsx
+import { loggerContext } from 'evlog/react-router'
+
+export async function loader({ params, context }: Route.LoaderArgs) {
+ const log = context.get(loggerContext)
+ log.set({ user: { id: params.id } })
+ return { users: [] }
+}
+```
+
+Use `useLogger()` to access the logger from anywhere in the call stack without passing context:
+
+```typescript
+import { useLogger } from 'evlog/react-router'
+
+async function findUsers() {
+ const log = useLogger()
+ log.set({ db: { query: 'SELECT * FROM users' } })
+}
+```
+
+Full pipeline with drain, enrich, and tail sampling:
+
+```typescript
+import { createAxiomDrain } from 'evlog/axiom'
+
+export const middleware: Route.MiddlewareFunction[] = [
+ evlog({
+ include: ['/api/**'],
+ drain: createAxiomDrain(),
+ enrich: (ctx) => { ctx.event.region = process.env.FLY_REGION },
+ keep: (ctx) => {
+ if (ctx.duration && ctx.duration > 2000) ctx.shouldKeep = true
+ },
+ }),
+]
+```
+
### Cloudflare Workers
```typescript