From 90a793a1db2b4aa77c125aace45ebea663764779 Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Thu, 27 Aug 2026 19:31:27 +0200
Subject: [PATCH 01/28] chore: adopt TypeScript 7, pnpm catalogs, and Turborepo
2.10
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Develop the library on TypeScript 7 while keeping the consumer peer at 5.9–7, and pin shared toolchain versions through a strict pnpm catalog so CI and local installs stay aligned.
Co-authored-by: Cursor
---
.github/workflows/lint-check.yml | 9 +-
.github/workflows/npm-publish.yml | 3 +-
.github/workflows/types-check.yml | 43 +-
.gitignore | 1 +
.vscode/settings.json | 5 +-
docs/content/docs/index.mdx | 1 +
docs/content/docs/migration-v3-to-v4.mdx | 2 +-
docs/package.json | 18 +-
examples/enum-based/package.json | 14 +-
examples/express-trpc-react/package.json | 15 +-
examples/express-trpc-react/tsconfig.json | 1 -
examples/express/package.json | 3 +-
examples/feature-flags/package.json | 14 +-
examples/next/package.json | 12 +-
examples/react/package.json | 14 +-
examples/rebac/package.json | 6 +-
examples/role-based/package.json | 14 +-
examples/solid/package.json | 4 +-
examples/svelte/package.json | 6 +-
examples/tanstack-start/package.json | 18 +-
examples/vue/package.json | 8 +-
ignores.ts | 1 +
oxlint.config.ts | 6 +
package.json | 24 +-
permix/package.json | 31 +-
permix/scripts/register-typescript6.mjs | 26 +
permix/scripts/smoke-exports.ts | 24 +
permix/src/core/check.ts | 28 +-
permix/src/core/errors.ts | 4 +-
permix/src/core/permix.test.ts | 18 +-
permix/src/hono/permix.ts | 3 +-
permix/src/node/permix.test.ts | 2 +-
permix/test-d/public-api.ts | 74 +
permix/tsconfig.base.json | 20 +-
permix/tsconfig.compat.json | 18 +
permix/tsconfig.react.json | 3 +-
permix/tsconfig.solid.json | 3 +-
pnpm-lock.yaml | 1530 +++++++++++----------
pnpm-workspace.yaml | 31 +-
turbo.json | 33 +-
40 files changed, 1213 insertions(+), 877 deletions(-)
create mode 100644 permix/scripts/register-typescript6.mjs
create mode 100644 permix/scripts/smoke-exports.ts
create mode 100644 permix/test-d/public-api.ts
create mode 100644 permix/tsconfig.compat.json
diff --git a/.github/workflows/lint-check.yml b/.github/workflows/lint-check.yml
index 7e646a89..39fecf19 100644
--- a/.github/workflows/lint-check.yml
+++ b/.github/workflows/lint-check.yml
@@ -16,15 +16,14 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
-
- - name: Setup pnpm
- uses: pnpm/action-setup@v4
- with:
- version: 11.5.0
+ cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml
index 47318b17..53cb9944 100644
--- a/.github/workflows/npm-publish.yml
+++ b/.github/workflows/npm-publish.yml
@@ -15,7 +15,8 @@ jobs:
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
- node-version: latest
+ node-version: 24
+ cache: pnpm
registry-url: 'https://registry.npmjs.org'
- run: pnpm i --frozen-lockfile
- run: pnpm run lint
diff --git a/.github/workflows/types-check.yml b/.github/workflows/types-check.yml
index 0f555beb..9f5c2de6 100644
--- a/.github/workflows/types-check.yml
+++ b/.github/workflows/types-check.yml
@@ -16,21 +16,52 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
+ cache: pnpm
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Check types
+ run: pnpm run check-types
+
+ typescript-compatibility:
+ name: TypeScript ${{ matrix.version }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - version: '5.9.3'
+ script: type-check:compat:5.9
+ - version: '6.0.2'
+ script: type-check:compat:6
+ - version: '7.0.2'
+ script: type-check:compat:7
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
with:
- version: 11.5.0
+ node-version: 24
+ cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- - name: Build
- run: pnpm run build
-
- - name: Check types
- run: pnpm run check-types
+ - name: Check source and published package
+ run: pnpm --filter permix ${{ matrix.script }}
diff --git a/.gitignore b/.gitignore
index e812c3e6..5d09cfd3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
node_modules
.DS_Store
.pnpm-store
+.turbo
diff --git a/.vscode/settings.json b/.vscode/settings.json
index fa404b73..0e37544a 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -14,5 +14,8 @@
},
"[jsonc]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
- }
+ },
+ "js/ts.experimental.useTsgo": true,
+ "js/ts.tsdk.path": "./node_modules/typescript",
+ "typescript.preferences.preferTypeOnlyAutoImports": true
}
diff --git a/docs/content/docs/index.mdx b/docs/content/docs/index.mdx
index 50db3dba..f2461726 100644
--- a/docs/content/docs/index.mdx
+++ b/docs/content/docs/index.mdx
@@ -166,6 +166,7 @@ const canUpdateComment = permix.check('comment.update', comment)
What are the benefits of using Permix?
- 100% type-safe without writing TypeScript (except for initialization)
+- Requires TypeScript 5.9 or newer (5.9, 6, and 7 are supported)
- Single source of truth for your entire app
- Perfect match for TypeScript monorepos
- Zero dependencies
diff --git a/docs/content/docs/migration-v3-to-v4.mdx b/docs/content/docs/migration-v3-to-v4.mdx
index 20be84ca..7918de34 100644
--- a/docs/content/docs/migration-v3-to-v4.mdx
+++ b/docs/content/docs/migration-v3-to-v4.mdx
@@ -16,7 +16,7 @@ This guide summarizes the breaking changes and how to update your app. For the f
permix@^4
```
-v4 is developed with **TypeScript 6**. Your app does not need to match the monorepo's exact TypeScript or pnpm versions, but upgrade TypeScript if you hit inference issues.
+v4 is developed with **TypeScript 7** and supports **TypeScript 5.9–7**. Your app does not need to match the monorepo's exact TypeScript or pnpm versions, but upgrade TypeScript if you hit inference issues.
## Quick reference
diff --git a/docs/package.json b/docs/package.json
index 7b7c16f4..2457c76c 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -9,8 +9,6 @@
"check-types": "fumadocs-mdx && tsc --noEmit",
"dev": "vite dev",
"postinstall": "fumadocs-mdx",
- "prebuild": "cd ../permix && pnpm run build",
- "predeploy": "cd ../permix && pnpm run build",
"preview": "vite preview",
"start": "node .output/server/index.mjs"
},
@@ -26,10 +24,10 @@
"fumadocs-ui": "^16.9.3",
"mermaid": "^11.15.0",
"permix": "workspace:*",
- "react": "^19.2.6",
- "react-dom": "^19.2.6",
+ "react": "catalog:",
+ "react-dom": "catalog:",
"tailwind-merge": "^3.6.0",
- "vite": "^8.0.16"
+ "vite": "catalog:"
},
"devDependencies": {
"@orpc/server": "^1.14.4",
@@ -37,10 +35,10 @@
"@trpc/server": "^11.17.0",
"@types/express": "^5.0.6",
"@types/mdx": "^2.0.13",
- "@types/node": "^24.10.0",
- "@types/react": "^19.2.15",
- "@types/react-dom": "^19.2.3",
- "@vitejs/plugin-react": "^6.0.2",
+ "@types/node": "catalog:",
+ "@types/react": "catalog:",
+ "@types/react-dom": "catalog:",
+ "@vitejs/plugin-react": "catalog:",
"drizzle-orm": "^1.0.0-rc.3",
"elysia": "^1.4.28",
"express": "^5",
@@ -48,6 +46,6 @@
"hono": "^4.12.23",
"nitro": "^3.0.260522-beta",
"tailwindcss": "^4.3.0",
- "typescript": "^6.0.3"
+ "typescript": "catalog:"
}
}
diff --git a/examples/enum-based/package.json b/examples/enum-based/package.json
index fa5a72a9..dabe0ff2 100644
--- a/examples/enum-based/package.json
+++ b/examples/enum-based/package.json
@@ -10,14 +10,14 @@
},
"dependencies": {
"permix": "workspace:*",
- "react": "^19.2.0",
- "react-dom": "^19.2.6"
+ "react": "catalog:",
+ "react-dom": "catalog:"
},
"devDependencies": {
- "@types/react": "^19.2.15",
- "@types/react-dom": "^19.2.3",
- "@vitejs/plugin-react": "^6.0.2",
- "typescript": "^6.0.3",
- "vite": "^8.0.16"
+ "@types/react": "catalog:",
+ "@types/react-dom": "catalog:",
+ "@vitejs/plugin-react": "catalog:",
+ "typescript": "catalog:",
+ "vite": "catalog:"
}
}
diff --git a/examples/express-trpc-react/package.json b/examples/express-trpc-react/package.json
index 617a0261..3d6f96ac 100644
--- a/examples/express-trpc-react/package.json
+++ b/examples/express-trpc-react/package.json
@@ -14,19 +14,20 @@
"cors": "^2.8.6",
"express": "^5.1.0",
"permix": "workspace:*",
- "react": "^19.2.0",
- "react-dom": "^19.2.6",
+ "react": "catalog:",
+ "react-dom": "catalog:",
"zod": "^4.4.3"
},
"devDependencies": {
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"@types/pg": "^8.20.0",
- "@types/react": "^19.2.15",
- "@types/react-dom": "^19.2.3",
- "@vitejs/plugin-react": "^6.0.2",
- "tsx": "^4.22.4",
- "vite": "^8.0.16",
+ "@types/react": "catalog:",
+ "@types/react-dom": "catalog:",
+ "@vitejs/plugin-react": "catalog:",
+ "tsx": "catalog:",
+ "typescript": "catalog:",
+ "vite": "catalog:",
"vite-tsconfig-paths": "^6.1.1"
}
}
diff --git a/examples/express-trpc-react/tsconfig.json b/examples/express-trpc-react/tsconfig.json
index 2c5b1e86..ece8100b 100644
--- a/examples/express-trpc-react/tsconfig.json
+++ b/examples/express-trpc-react/tsconfig.json
@@ -5,7 +5,6 @@
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"moduleDetection": "force",
"useDefineForClassFields": true,
- "ignoreDeprecations": "6.0",
"module": "ESNext",
"moduleResolution": "bundler",
"paths": {
diff --git a/examples/express/package.json b/examples/express/package.json
index 3270a084..7fce8c1b 100644
--- a/examples/express/package.json
+++ b/examples/express/package.json
@@ -12,6 +12,7 @@
},
"devDependencies": {
"@types/express": "^5.0.6",
- "tsx": "^4.22.4"
+ "tsx": "catalog:",
+ "typescript": "catalog:"
}
}
diff --git a/examples/feature-flags/package.json b/examples/feature-flags/package.json
index 1bef5b37..86ee93f6 100644
--- a/examples/feature-flags/package.json
+++ b/examples/feature-flags/package.json
@@ -10,14 +10,14 @@
},
"dependencies": {
"permix": "workspace:*",
- "react": "^19.2.0",
- "react-dom": "^19.2.6"
+ "react": "catalog:",
+ "react-dom": "catalog:"
},
"devDependencies": {
- "@types/react": "^19.2.15",
- "@types/react-dom": "^19.2.3",
- "@vitejs/plugin-react": "^6.0.2",
- "typescript": "^6.0.3",
- "vite": "^8.0.16"
+ "@types/react": "catalog:",
+ "@types/react-dom": "catalog:",
+ "@vitejs/plugin-react": "catalog:",
+ "typescript": "catalog:",
+ "vite": "catalog:"
}
}
diff --git a/examples/next/package.json b/examples/next/package.json
index ab95c31f..b11fd741 100644
--- a/examples/next/package.json
+++ b/examples/next/package.json
@@ -10,15 +10,15 @@
"dependencies": {
"next": "16.2.6",
"permix": "workspace:*",
- "react": "19.2.4",
- "react-dom": "19.2.4"
+ "react": "catalog:",
+ "react-dom": "catalog:"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.3.0",
- "@types/node": "^25.9.1",
- "@types/react": "^19.2.15",
- "@types/react-dom": "^19.2.3",
+ "@types/node": "catalog:",
+ "@types/react": "catalog:",
+ "@types/react-dom": "catalog:",
"tailwindcss": "^4.3.0",
- "typescript": "^6.0.3"
+ "typescript": "catalog:"
}
}
diff --git a/examples/react/package.json b/examples/react/package.json
index 02a32663..ad68ff5f 100644
--- a/examples/react/package.json
+++ b/examples/react/package.json
@@ -11,14 +11,14 @@
},
"dependencies": {
"permix": "workspace:*",
- "react": "^19.2.0",
- "react-dom": "^19.2.6"
+ "react": "catalog:",
+ "react-dom": "catalog:"
},
"devDependencies": {
- "@types/react": "^19.2.15",
- "@types/react-dom": "^19.2.3",
- "@vitejs/plugin-react": "^6.0.2",
- "typescript": "^6.0.3",
- "vite": "^8.0.16"
+ "@types/react": "catalog:",
+ "@types/react-dom": "catalog:",
+ "@vitejs/plugin-react": "catalog:",
+ "typescript": "catalog:",
+ "vite": "catalog:"
}
}
diff --git a/examples/rebac/package.json b/examples/rebac/package.json
index 9ff84d58..bc9ffcbe 100644
--- a/examples/rebac/package.json
+++ b/examples/rebac/package.json
@@ -10,8 +10,8 @@
"permix": "workspace:*"
},
"devDependencies": {
- "@types/node": "^25.9.1",
- "tsx": "^4.22.4",
- "typescript": "^6.0.3"
+ "@types/node": "catalog:",
+ "tsx": "catalog:",
+ "typescript": "catalog:"
}
}
diff --git a/examples/role-based/package.json b/examples/role-based/package.json
index 762829ac..f80ce699 100644
--- a/examples/role-based/package.json
+++ b/examples/role-based/package.json
@@ -10,14 +10,14 @@
},
"dependencies": {
"permix": "workspace:*",
- "react": "^19.2.0",
- "react-dom": "^19.2.6"
+ "react": "catalog:",
+ "react-dom": "catalog:"
},
"devDependencies": {
- "@types/react": "^19.2.15",
- "@types/react-dom": "^19.2.3",
- "@vitejs/plugin-react": "^6.0.2",
- "typescript": "^6.0.3",
- "vite": "^8.0.16"
+ "@types/react": "catalog:",
+ "@types/react-dom": "catalog:",
+ "@vitejs/plugin-react": "catalog:",
+ "typescript": "catalog:",
+ "vite": "catalog:"
}
}
diff --git a/examples/solid/package.json b/examples/solid/package.json
index 81dceebf..66574579 100644
--- a/examples/solid/package.json
+++ b/examples/solid/package.json
@@ -14,8 +14,8 @@
"solid-js": "^1.9.13"
},
"devDependencies": {
- "typescript": "^6.0.3",
- "vite": "^8.0.16",
+ "typescript": "catalog:",
+ "vite": "catalog:",
"vite-plugin-solid": "^2.11.12"
}
}
diff --git a/examples/svelte/package.json b/examples/svelte/package.json
index 5f6a066b..ed6afc06 100644
--- a/examples/svelte/package.json
+++ b/examples/svelte/package.json
@@ -20,10 +20,10 @@
"@sveltejs/adapter-auto": "^7.0.1",
"@sveltejs/kit": "^2.61.1",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
- "@types/node": "^25.9.1",
+ "@types/node": "catalog:",
"svelte": "^5.55.2",
"svelte-check": "^4.5.0",
- "typescript": "^6.0.3",
- "vite": "^8.0.16"
+ "typescript": "catalog:",
+ "vite": "catalog:"
}
}
diff --git a/examples/tanstack-start/package.json b/examples/tanstack-start/package.json
index 442ab683..ab539aed 100644
--- a/examples/tanstack-start/package.json
+++ b/examples/tanstack-start/package.json
@@ -22,8 +22,8 @@
"@tanstack/router-plugin": "^1.132.0",
"lucide-react": "^0.545.0",
"permix": "workspace:*",
- "react": "^19.2.0",
- "react-dom": "^19.2.0",
+ "react": "catalog:",
+ "react-dom": "catalog:",
"tailwindcss": "^4.1.18"
},
"devDependencies": {
@@ -32,13 +32,13 @@
"@tanstack/router-cli": "^1.132.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.0",
- "@types/node": "^22.10.2",
- "@types/react": "^19.2.0",
- "@types/react-dom": "^19.2.0",
- "@vitejs/plugin-react": "^6.0.1",
+ "@types/node": "catalog:",
+ "@types/react": "catalog:",
+ "@types/react-dom": "catalog:",
+ "@vitejs/plugin-react": "catalog:",
"jsdom": "^28.1.0",
- "typescript": "^6.0.2",
- "vite": "^8.0.0",
- "vitest": "^4.1.5"
+ "typescript": "catalog:",
+ "vite": "catalog:",
+ "vitest": "catalog:"
}
}
diff --git a/examples/vue/package.json b/examples/vue/package.json
index eef5064d..dad9a785 100644
--- a/examples/vue/package.json
+++ b/examples/vue/package.json
@@ -4,8 +4,8 @@
"private": true,
"type": "module",
"scripts": {
- "build": "vue-tsc -b && vite build",
- "check-types": "vue-tsc --noEmit",
+ "build": "node --import ../../permix/scripts/register-typescript6.mjs ./node_modules/vue-tsc/bin/vue-tsc.js -b && vite build",
+ "check-types": "node --import ../../permix/scripts/register-typescript6.mjs ./node_modules/vue-tsc/bin/vue-tsc.js --noEmit",
"dev": "vite",
"preview": "vite preview"
},
@@ -16,8 +16,8 @@
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.7",
"@vue/tsconfig": "^0.9.1",
- "typescript": "^6.0.3",
- "vite": "^8.0.16",
+ "typescript": "catalog:",
+ "vite": "catalog:",
"vue-tsc": "^3.3.3"
}
}
diff --git a/ignores.ts b/ignores.ts
index 9e77439e..8edf9972 100644
--- a/ignores.ts
+++ b/ignores.ts
@@ -9,6 +9,7 @@ export const ignorePatterns = [
'**/out/**',
'**/node_modules/**',
'**/_artifacts/**',
+ '**/.agents/**',
'**/next-env.d.ts',
'**/*.gen.ts',
'**/*.generated.ts',
diff --git a/oxlint.config.ts b/oxlint.config.ts
index e29a4dc3..56a74887 100644
--- a/oxlint.config.ts
+++ b/oxlint.config.ts
@@ -118,6 +118,12 @@ export default defineConfig({
},
overrides: [
...vitestOverrides,
+ {
+ files: ['permix/test-d/**'],
+ rules: {
+ 'typescript/consistent-type-definitions': 'off',
+ },
+ },
{
files: nonReactGlobs,
rules: {
diff --git a/package.json b/package.json
index 704b4041..d07f6b74 100644
--- a/package.json
+++ b/package.json
@@ -2,27 +2,31 @@
"private": true,
"type": "module",
"scripts": {
- "build": "cd permix && pnpm run build",
- "check-types": "turbo check-types",
+ "build": "turbo run build --filter=permix",
+ "check-types": "turbo run check-types",
"format": "oxfmt --config ./oxfmt.config.ts --ignore-path .gitignore .",
"format:check": "oxfmt --config ./oxfmt.config.ts --ignore-path .gitignore --check .",
"lint": "oxlint --type-aware --config ./oxlint.config.ts --ignore-path .gitignore --deny-warnings .",
"lint:fix": "oxlint --type-aware --config ./oxlint.config.ts --ignore-path .gitignore --fix --deny-warnings .",
"prepare": "husky",
"publish": "cd permix && pnpm publish",
- "test": "cd permix && pnpm run test",
+ "test": "turbo run test --filter=permix",
"update": "taze -r -I major"
},
"devDependencies": {
"husky": "^9.1.7",
"npm-run-all2": "^9.0.1",
- "oxfmt": "^0.64.0",
- "oxlint": "^1.79.0",
- "oxlint-tsgolint": "^7.0.2001",
+ "oxfmt": "catalog:",
+ "oxlint": "catalog:",
+ "oxlint-tsgolint": "catalog:",
"taze": "^19.14.1",
- "turbo": "^2.9.16",
- "typescript": "^6.0.3",
- "ultracite": "^7.10.6"
+ "turbo": "catalog:",
+ "typescript": "catalog:",
+ "ultracite": "catalog:"
},
- "packageManager": "pnpm@11.5.0"
+ "engines": {
+ "node": ">=22",
+ "pnpm": ">=11.0.0"
+ },
+ "packageManager": "pnpm@11.24.0"
}
diff --git a/permix/package.json b/permix/package.json
index dfbefd33..79f661aa 100644
--- a/permix/package.json
+++ b/permix/package.json
@@ -119,9 +119,13 @@
}
},
"scripts": {
- "build": "tsdown && pnpm run build:svelte",
- "build:svelte": "svelte-package -i src/svelte -o dist/svelte && node ./scripts/build-svelte.ts",
- "check-types": "tsc --build && svelte-check --tsconfig ./tsconfig.svelte.json",
+ "build": "node --import ./scripts/register-typescript6.mjs ./node_modules/tsdown/dist/run.mjs && pnpm run build:svelte",
+ "build:svelte": "node --import ./scripts/register-typescript6.mjs ./node_modules/@sveltejs/package/src/cli.js -i src/svelte -o dist/svelte && node ./scripts/build-svelte.ts",
+ "check-types": "tsc --build && node --import ./scripts/register-typescript6.mjs ./node_modules/svelte-check/bin/svelte-check --tsconfig ./tsconfig.svelte.json",
+ "type-check:compat:5.9": "pnpm run build && node ./node_modules/typescript59/bin/tsc --build && node ./node_modules/typescript59/bin/tsc -p tsconfig.compat.json && node ./scripts/smoke-exports.ts",
+ "type-check:compat:6": "pnpm run build && node ./node_modules/typescript6/bin/tsc6 --build && node ./node_modules/typescript6/bin/tsc6 -p tsconfig.compat.json --stableTypeOrdering && node ./scripts/smoke-exports.ts",
+ "type-check:compat:7": "pnpm run build && tsc --build && tsc -p tsconfig.compat.json && node ./scripts/smoke-exports.ts",
+ "type-check:compat": "pnpm run type-check:compat:5.9 && pnpm run type-check:compat:6 && pnpm run type-check:compat:7",
"prepublishOnly": "run-s check-types test build scripts:copy-readme skills:validate",
"scripts:copy-readme": "node ./scripts/copy-readme.ts",
"skills:stale": "intent stale skills",
@@ -138,23 +142,26 @@
"@testing-library/react": "^16.3.2",
"@testing-library/svelte": "^5.3.1",
"@types/express": "^5.0.6",
- "@types/node": "^25.9.1",
- "@types/react": "^19.2.15",
+ "@types/node": "catalog:",
+ "@types/react": "catalog:",
"@types/supertest": "^7.2.0",
- "@vitejs/plugin-react": "^6.0.2",
+ "@vitejs/plugin-react": "catalog:",
"@vitest/coverage-v8": "^4.1.8",
"@vue/test-utils": "^2.4.10",
"drizzle-orm": "1.0.0-rc.3",
"effect": "^3.21.2",
"happy-dom": "^20.9.0",
- "react-dom": "^19.2.6",
+ "react": "catalog:",
+ "react-dom": "catalog:",
"supertest": "^7.2.2",
"svelte": "^5.56.0",
"svelte-check": "^4.5.0",
"tsdown": "^0.22.1",
- "typescript": "^6.0.3",
+ "typescript": "catalog:",
+ "typescript59": "catalog:typescript-classic",
+ "typescript6": "catalog:typescript-classic",
"vite-plugin-solid": "^2.11.12",
- "vitest": "^4.1.8",
+ "vitest": "catalog:",
"vue": "^3.5.17",
"zod": "^4.4.3"
},
@@ -174,6 +181,7 @@
"react-dom": ">=18",
"solid-js": ">=1",
"svelte": ">=5",
+ "typescript": ">=5.9 <8",
"vue": ">=3"
},
"peerDependenciesMeta": {
@@ -224,12 +232,15 @@
},
"vue": {
"optional": true
+ },
+ "typescript": {
+ "optional": true
}
},
"engines": {
"node": ">=22"
},
- "packageManager": "pnpm@11.5.0",
+ "packageManager": "pnpm@11.24.0",
"intent": {
"repo": "letstri/permix",
"docs": "../docs/content/docs"
diff --git a/permix/scripts/register-typescript6.mjs b/permix/scripts/register-typescript6.mjs
new file mode 100644
index 00000000..7fa35848
--- /dev/null
+++ b/permix/scripts/register-typescript6.mjs
@@ -0,0 +1,26 @@
+import { createRequire, registerHooks } from 'node:module'
+import { pathToFileURL } from 'node:url'
+
+const require = createRequire(import.meta.url)
+
+registerHooks({
+ resolve(specifier, context, nextResolve) {
+ if (specifier === 'typescript/lib/tsc') {
+ // vue-tsc patches the TS 5.9 tsc shim; TS 6/7 lib/tsc is not patchable.
+ return {
+ shortCircuit: true,
+ url: pathToFileURL(require.resolve('typescript59/lib/tsc.js')).href,
+ }
+ }
+
+ if (specifier === 'typescript' || specifier.startsWith('typescript/')) {
+ const mapped = specifier.replace(/^typescript(?=\/|$)/, 'typescript6')
+ return {
+ shortCircuit: true,
+ url: pathToFileURL(require.resolve(mapped)).href,
+ }
+ }
+
+ return nextResolve(specifier, context)
+ },
+})
diff --git a/permix/scripts/smoke-exports.ts b/permix/scripts/smoke-exports.ts
new file mode 100644
index 00000000..03e74e4c
--- /dev/null
+++ b/permix/scripts/smoke-exports.ts
@@ -0,0 +1,24 @@
+const entrypoints = [
+ ['.', await import('permix'), ['createPermix']],
+ ['./trpc', await import('permix/trpc'), ['createPermix']],
+ ['./orpc', await import('permix/orpc'), ['createPermix']],
+ ['./express', await import('permix/express'), ['createPermix']],
+ ['./hono', await import('permix/hono'), ['createPermix']],
+ ['./node', await import('permix/node'), ['createPermix']],
+ ['./server', await import('permix/server'), ['createPermix']],
+ ['./elysia', await import('permix/elysia'), ['createPermix']],
+ ['./fastify', await import('permix/fastify'), ['createPermix']],
+ ['./drizzle', await import('permix/drizzle'), ['createPermix']],
+ ['./drizzle/legacy', await import('permix/drizzle/legacy'), ['createPermix']],
+ ['./effect', await import('permix/effect'), ['createPermix']],
+] as const
+
+for (const [subpath, module, expectedExports] of entrypoints) {
+ for (const exportName of expectedExports) {
+ if (!(exportName in module)) {
+ throw new Error(
+ `Missing ${exportName} export from permix${subpath === '.' ? '' : subpath}`
+ )
+ }
+ }
+}
diff --git a/permix/src/core/check.ts b/permix/src/core/check.ts
index cff802b9..f5d07e90 100644
--- a/permix/src/core/check.ts
+++ b/permix/src/core/check.ts
@@ -51,10 +51,14 @@ function walk(rules: Rules, inputArgs: unknown[]): boolean {
const last = parts.at(-1)
if (isSpecialSymbol(last)) {
- let subtree: Rule = rules
+ let subtree: Rule | undefined = rules
for (let i = 0; i < parts.length - 1; i++) {
+ const segment = parts[i]
+ if (segment === undefined) {
+ break
+ }
if (subtree && typeof subtree === 'object') {
- subtree = (subtree as Record)[parts[i]]
+ subtree = (subtree as Record)[segment]
}
}
@@ -72,7 +76,10 @@ function walk(rules: Rules, inputArgs: unknown[]): boolean {
return void out.push(callRuleWithoutData(rule))
}
for (const key in rule) {
- visit(rule[key])
+ const child = rule[key]
+ if (child !== undefined) {
+ visit(child)
+ }
}
}
visit(subtree)
@@ -84,10 +91,14 @@ function walk(rules: Rules, inputArgs: unknown[]): boolean {
}
}
- let rule: Rule = rules
+ let rule: Rule | undefined = rules
let i = 0
for (; i < args.length && typeof rule === 'object'; i++) {
- rule = rule[String(args[i])]
+ const arg = args[i]
+ if (typeof arg !== 'string') {
+ break
+ }
+ rule = (rule as Record)[arg]
}
if (typeof rule === 'boolean') {
@@ -142,5 +153,10 @@ export function createCheckContext(
return { path: first }
}
- return { path: first, data: params[1] }
+ const data = params[1]
+ if (data === undefined) {
+ return { path: first }
+ }
+
+ return { path: first, data }
}
diff --git a/permix/src/core/errors.ts b/permix/src/core/errors.ts
index 19276dfd..225566a0 100644
--- a/permix/src/core/errors.ts
+++ b/permix/src/core/errors.ts
@@ -28,7 +28,9 @@ export class PermixNotFoundError extends PermixError {
constructor(key?: string | symbol) {
super('Instance not found. Please setup the permix instance first.')
this.name = 'PermixNotFoundError'
- this.key = key
+ if (key !== undefined) {
+ this.key = key
+ }
}
}
diff --git a/permix/src/core/permix.test.ts b/permix/src/core/permix.test.ts
index aef3d742..676f4110 100644
--- a/permix/src/core/permix.test.ts
+++ b/permix/src/core/permix.test.ts
@@ -139,16 +139,18 @@ describe(createPermix, () => {
expect(permix.check('post.create')).toBe(true)
})
- it('should work with enum-based permissions', () => {
- enum PostAction {
- Create = 'create',
- Read = 'read',
- Update = 'update',
- Delete = 'delete',
- }
+ it('should work with enum-like permissions', () => {
+ const PostAction = {
+ Create: 'create',
+ Read: 'read',
+ Update: 'update',
+ Delete: 'delete',
+ } as const
+
+ type PostActionName = (typeof PostAction)[keyof typeof PostAction]
const permix = createPermix<{
- post: [PostAction]
+ post: [PostActionName]
}>()
permix.setup({
diff --git a/permix/src/hono/permix.ts b/permix/src/hono/permix.ts
index 049bcc5f..9c6c8cf7 100644
--- a/permix/src/hono/permix.ts
+++ b/permix/src/hono/permix.ts
@@ -93,7 +93,8 @@ function buildPermix(
return await onForbidden({ c, ...createCheckContext(...args) })
}
- await next()
+ // oxlint-disable-next-line typescript/no-confusing-void-expression -- Hono's next() is Promise
+ return await next()
})
function getRules(c: Context): Rules | null {
diff --git a/permix/src/node/permix.test.ts b/permix/src/node/permix.test.ts
index 18165c19..5d3c1680 100644
--- a/permix/src/node/permix.test.ts
+++ b/permix/src/node/permix.test.ts
@@ -325,7 +325,7 @@ describe('checkMiddleware without setupMiddleware', () => {
await permix.checkMiddleware('post.create')(req, res, next)
expect(next).toHaveBeenCalledOnce()
- expect(next.mock.calls[0][0]).toBeInstanceOf(PermixNotFoundError)
+ expect(next.mock.calls[0]?.[0]).toBeInstanceOf(PermixNotFoundError)
})
})
diff --git a/permix/test-d/public-api.ts b/permix/test-d/public-api.ts
new file mode 100644
index 00000000..2bbcaf9d
--- /dev/null
+++ b/permix/test-d/public-api.ts
@@ -0,0 +1,74 @@
+import type { CheckArgs } from 'permix'
+import { createPermix } from 'permix'
+import { createPermix as createDrizzlePermix } from 'permix/drizzle'
+import { createPermix as createDrizzleLegacyPermix } from 'permix/drizzle/legacy'
+import { createPermix as createEffectPermix } from 'permix/effect'
+import { createPermix as createElysiaPermix } from 'permix/elysia'
+import { createPermix as createExpressPermix } from 'permix/express'
+import { createPermix as createFastifyPermix } from 'permix/fastify'
+import { createPermix as createHonoPermix } from 'permix/hono'
+import { createPermix as createNextPermix } from 'permix/next'
+import { createPermix as createNodePermix } from 'permix/node'
+import { createPermix as createOrpcPermix } from 'permix/orpc'
+import { createComponents as createReactComponents } from 'permix/react'
+import { createPermix as createServerPermix } from 'permix/server'
+import { createComponents as createSolidComponents } from 'permix/solid'
+import { createComponents as createSvelteComponents } from 'permix/svelte'
+import { createPermix as createTanstackStartPermix } from 'permix/tanstack-start'
+import { createPermix as createTrpcPermix } from 'permix/trpc'
+import { createComponents as createVueComponents } from 'permix/vue'
+
+type PostDefinition = {
+ post: ['create', 'read']
+}
+
+const core = createPermix()
+const react = createReactComponents(core)
+const vue = createVueComponents(core)
+const trpc = createTrpcPermix()
+const orpc = createOrpcPermix()
+const express = createExpressPermix()
+const hono = createHonoPermix()
+const node = createNodePermix()
+const server = createServerPermix()
+const elysia = createElysiaPermix()
+const fastify = createFastifyPermix()
+const solid = createSolidComponents(core)
+const svelte = createSvelteComponents(core)
+const drizzle = createDrizzlePermix({})
+const drizzleLegacy = createDrizzleLegacyPermix({})
+const effect = createEffectPermix()
+const next = createNextPermix()
+const tanstackStart = createTanstackStartPermix()
+
+core.setup({
+ post: {
+ create: true,
+ read: true,
+ },
+})
+
+const checkArgs: CheckArgs = ['post.create']
+const allowed = core.check(...checkArgs)
+
+export {
+ allowed,
+ core,
+ drizzle,
+ drizzleLegacy,
+ effect,
+ elysia,
+ express,
+ fastify,
+ hono,
+ next,
+ node,
+ orpc,
+ react,
+ server,
+ solid,
+ svelte,
+ tanstackStart,
+ trpc,
+ vue,
+}
diff --git a/permix/tsconfig.base.json b/permix/tsconfig.base.json
index cf0229e7..361a91ca 100644
--- a/permix/tsconfig.base.json
+++ b/permix/tsconfig.base.json
@@ -1,13 +1,27 @@
{
"compilerOptions": {
"target": "ESNext",
+ "lib": ["ESNext"],
"jsx": "preserve",
"module": "ESNext",
"moduleResolution": "Bundler",
+ "moduleDetection": "force",
"strict": true,
+ "exactOptionalPropertyTypes": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true,
+ "allowUnreachableCode": false,
+ "allowUnusedLabels": false,
+ "verbatimModuleSyntax": true,
+ "isolatedModules": true,
+ "erasableSyntaxOnly": true,
"noEmit": true,
"forceConsistentCasingInFileNames": true,
- "skipLibCheck": true
+ "skipLibCheck": true,
+ "types": []
},
"include": ["src/**/*.ts", "src/**/*.tsx", "*.ts"],
"exclude": [
@@ -20,6 +34,8 @@
"src/tanstack-start/*.ts",
"src/tanstack-start/*.tsx",
"src/svelte/**/*.ts",
- "src/svelte/**/*.svelte"
+ "src/svelte/**/*.svelte",
+ "test-d/**",
+ "scripts/**"
]
}
diff --git a/permix/tsconfig.compat.json b/permix/tsconfig.compat.json
new file mode 100644
index 00000000..292210be
--- /dev/null
+++ b/permix/tsconfig.compat.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "lib": ["ESNext", "DOM"],
+ "target": "ESNext",
+ "module": "Preserve",
+ "moduleResolution": "bundler",
+ "moduleDetection": "force",
+ "strict": true,
+ "exactOptionalPropertyTypes": true,
+ "noEmit": true,
+ "noUncheckedIndexedAccess": true,
+ "noUncheckedSideEffectImports": true,
+ "skipLibCheck": true,
+ "types": [],
+ "verbatimModuleSyntax": true
+ },
+ "include": ["test-d/**/*.ts"]
+}
diff --git a/permix/tsconfig.react.json b/permix/tsconfig.react.json
index 9073ac9e..27ef9c67 100644
--- a/permix/tsconfig.react.json
+++ b/permix/tsconfig.react.json
@@ -3,7 +3,8 @@
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react",
- "lib": ["ESNext", "DOM", "DOM.Iterable"]
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
+ "types": []
},
"include": [
"src/react/*.ts",
diff --git a/permix/tsconfig.solid.json b/permix/tsconfig.solid.json
index 3e543a59..7a78aaf6 100644
--- a/permix/tsconfig.solid.json
+++ b/permix/tsconfig.solid.json
@@ -3,7 +3,8 @@
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "solid-js",
- "lib": ["ESNext", "DOM", "DOM.Iterable"]
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
+ "types": []
},
"include": ["src/solid/*.ts", "src/solid/*.tsx"],
"exclude": ["src/react/*.ts", "src/react/*.tsx"]
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 761d130a..f12eb59a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4,6 +4,61 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
+catalogs:
+ default:
+ '@types/node':
+ specifier: ^25.9.1
+ version: 25.9.1
+ '@types/react':
+ specifier: ^19.2.15
+ version: 19.2.15
+ '@types/react-dom':
+ specifier: ^19.2.3
+ version: 19.2.3
+ '@vitejs/plugin-react':
+ specifier: ^6.0.2
+ version: 6.0.2
+ oxfmt:
+ specifier: ^0.64.0
+ version: 0.64.0
+ oxlint:
+ specifier: ^1.79.0
+ version: 1.80.0
+ oxlint-tsgolint:
+ specifier: ^7.0.2001
+ version: 7.0.2001
+ react:
+ specifier: ^19.2.6
+ version: 19.2.6
+ react-dom:
+ specifier: ^19.2.6
+ version: 19.2.6
+ tsx:
+ specifier: ^4.22.4
+ version: 4.22.4
+ turbo:
+ specifier: 2.10.12
+ version: 2.10.12
+ typescript:
+ specifier: 7.0.2
+ version: 7.0.2
+ ultracite:
+ specifier: ^7.10.6
+ version: 7.10.6
+ vite:
+ specifier: ^8.0.16
+ version: 8.0.16
+ vitest:
+ specifier: ^4.1.8
+ version: 4.1.8
+ typescript-classic:
+ typescript59:
+ specifier: npm:typescript@5.9.3
+ version: 5.9.3
+ typescript6:
+ specifier: npm:@typescript/typescript6@6.0.2
+ version: 6.0.2
+
importers:
.:
@@ -15,25 +70,25 @@ importers:
specifier: ^9.0.1
version: 9.0.1
oxfmt:
- specifier: ^0.64.0
+ specifier: 'catalog:'
version: 0.64.0(svelte@5.56.0)
oxlint:
- specifier: ^1.79.0
+ specifier: 'catalog:'
version: 1.80.0(oxlint-tsgolint@7.0.2001)
oxlint-tsgolint:
- specifier: ^7.0.2001
+ specifier: 'catalog:'
version: 7.0.2001
taze:
specifier: ^19.14.1
version: 19.14.1
turbo:
- specifier: ^2.9.16
- version: 2.9.16
+ specifier: 'catalog:'
+ version: 2.10.12
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
ultracite:
- specifier: ^7.10.6
+ specifier: 'catalog:'
version: 7.10.6(oxfmt@0.64.0(svelte@5.56.0))(oxlint@1.80.0(oxlint-tsgolint@7.0.2001))
docs:
@@ -49,22 +104,22 @@ importers:
version: 1.167.0(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start':
specifier: ^1.168.14
- version: 1.168.24(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 1.168.24(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vercel/analytics':
specifier: ^2.0.1
- version: 2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@6.0.3))
+ version: 2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@7.0.2))
fumadocs-core:
specifier: ^16.9.3
- version: 16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
+ version: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
fumadocs-mdx:
specifier: ^15.0.10
- version: 15.0.10(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 15.0.10(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
fumadocs-twoslash:
specifier: ^3.2.0
- version: 3.2.0(1d030e86b7f7b9132e23a7c3ab6b57d0)
+ version: 3.2.0(b4a625e5e99ec7f2a6e049efc2c17627)
fumadocs-ui:
specifier: ^16.9.3
- version: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)
+ version: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)
mermaid:
specifier: ^11.15.0
version: 11.15.0
@@ -72,27 +127,27 @@ importers:
specifier: workspace:*
version: link:../permix
react:
- specifier: ^19.2.6
+ specifier: 'catalog:'
version: 19.2.6
react-dom:
- specifier: ^19.2.6
+ specifier: 'catalog:'
version: 19.2.6(react@19.2.6)
tailwind-merge:
specifier: ^3.6.0
version: 3.6.0
vite:
- specifier: ^8.0.16
- version: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ specifier: 'catalog:'
+ version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
devDependencies:
'@orpc/server':
specifier: ^1.14.4
version: 1.14.4(crossws@0.4.5(srvx@0.11.16))(fastify@5.8.5)(ws@8.21.0)
'@tailwindcss/vite':
specifier: ^4.3.0
- version: 4.3.0(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 4.3.0(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@trpc/server':
specifier: ^11.17.0
- version: 11.17.0(typescript@6.0.3)
+ version: 11.17.0(typescript@7.0.2)
'@types/express':
specifier: ^5.0.6
version: 5.0.6
@@ -100,26 +155,26 @@ importers:
specifier: ^2.0.13
version: 2.0.13
'@types/node':
- specifier: ^24.10.0
- version: 24.10.0
+ specifier: 'catalog:'
+ version: 25.9.1
'@types/react':
- specifier: ^19.2.15
+ specifier: 'catalog:'
version: 19.2.15
'@types/react-dom':
- specifier: ^19.2.3
+ specifier: 'catalog:'
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
- specifier: ^6.0.2
- version: 6.0.2(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ specifier: 'catalog:'
+ version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
drizzle-orm:
specifier: ^1.0.0-rc.3
version: 1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(effect@3.21.2)(zod@4.4.3)
elysia:
specifier: ^1.4.28
- version: 1.4.28(@sinclair/typebox@0.34.49)(exact-mirror@1.0.0)(file-type@22.0.1)(openapi-types@12.1.3)(typescript@6.0.3)
+ version: 1.4.28(@sinclair/typebox@0.34.49)(exact-mirror@1.0.0)(file-type@22.0.1(supports-color@7.2.0))(openapi-types@12.1.3)(typescript@7.0.2)
express:
specifier: ^5
- version: 5.2.1
+ version: 5.2.1(supports-color@7.2.0)
fastify:
specifier: ^5.8.5
version: 5.8.5
@@ -128,13 +183,13 @@ importers:
version: 4.12.23
nitro:
specifier: ^3.0.260522-beta
- version: 3.0.260522-beta(chokidar@5.0.0)(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3))(jiti@2.7.0)(lru-cache@11.5.1)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 3.0.260522-beta(chokidar@5.0.0)(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3))(jiti@2.7.0)(lru-cache@11.5.1)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
tailwindcss:
specifier: ^4.3.0
version: 4.3.0
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
examples/enum-based:
dependencies:
@@ -142,33 +197,33 @@ importers:
specifier: workspace:*
version: link:../../permix
react:
- specifier: ^19.2.0
+ specifier: 'catalog:'
version: 19.2.6
react-dom:
- specifier: ^19.2.6
+ specifier: 'catalog:'
version: 19.2.6(react@19.2.6)
devDependencies:
'@types/react':
- specifier: ^19.2.15
+ specifier: 'catalog:'
version: 19.2.15
'@types/react-dom':
- specifier: ^19.2.3
+ specifier: 'catalog:'
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
- specifier: ^6.0.2
+ specifier: 'catalog:'
version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
vite:
- specifier: ^8.0.16
+ specifier: 'catalog:'
version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
examples/express:
dependencies:
express:
specifier: ^5.2.1
- version: 5.2.1
+ version: 5.2.1(supports-color@7.2.0)
permix:
specifier: workspace:*
version: link:../../permix
@@ -177,31 +232,34 @@ importers:
specifier: ^5.0.6
version: 5.0.6
tsx:
- specifier: ^4.22.4
+ specifier: 'catalog:'
version: 4.22.4
+ typescript:
+ specifier: 'catalog:'
+ version: 7.0.2
examples/express-trpc-react:
dependencies:
'@trpc/client':
specifier: ^11.17.0
- version: 11.17.0(@trpc/server@11.17.0(typescript@6.0.3))(typescript@6.0.3)
+ version: 11.17.0(@trpc/server@11.17.0(typescript@7.0.2))(typescript@7.0.2)
'@trpc/server':
specifier: ^11.17.0
- version: 11.17.0(typescript@6.0.3)
+ version: 11.17.0(typescript@7.0.2)
cors:
specifier: ^2.8.6
version: 2.8.6
express:
specifier: ^5.1.0
- version: 5.2.1
+ version: 5.2.1(supports-color@7.2.0)
permix:
specifier: workspace:*
version: link:../../permix
react:
- specifier: ^19.2.0
+ specifier: 'catalog:'
version: 19.2.6
react-dom:
- specifier: ^19.2.6
+ specifier: 'catalog:'
version: 19.2.6(react@19.2.6)
zod:
specifier: ^4.4.3
@@ -217,23 +275,26 @@ importers:
specifier: ^8.20.0
version: 8.20.0
'@types/react':
- specifier: ^19.2.15
+ specifier: 'catalog:'
version: 19.2.15
'@types/react-dom':
- specifier: ^19.2.3
+ specifier: 'catalog:'
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
- specifier: ^6.0.2
+ specifier: 'catalog:'
version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
tsx:
- specifier: ^4.22.4
+ specifier: 'catalog:'
version: 4.22.4
+ typescript:
+ specifier: 'catalog:'
+ version: 7.0.2
vite:
- specifier: ^8.0.16
+ specifier: 'catalog:'
version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
vite-tsconfig-paths:
specifier: ^6.1.1
- version: 6.1.1(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 6.1.1(supports-color@7.2.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
examples/feature-flags:
dependencies:
@@ -241,61 +302,61 @@ importers:
specifier: workspace:*
version: link:../../permix
react:
- specifier: ^19.2.0
+ specifier: 'catalog:'
version: 19.2.6
react-dom:
- specifier: ^19.2.6
+ specifier: 'catalog:'
version: 19.2.6(react@19.2.6)
devDependencies:
'@types/react':
- specifier: ^19.2.15
+ specifier: 'catalog:'
version: 19.2.15
'@types/react-dom':
- specifier: ^19.2.3
+ specifier: 'catalog:'
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
- specifier: ^6.0.2
+ specifier: 'catalog:'
version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
vite:
- specifier: ^8.0.16
+ specifier: 'catalog:'
version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
examples/next:
dependencies:
next:
specifier: 16.2.6
- version: 16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
+ version: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
permix:
specifier: workspace:*
version: link:../../permix
react:
- specifier: 19.2.4
- version: 19.2.4
+ specifier: 'catalog:'
+ version: 19.2.6
react-dom:
- specifier: 19.2.4
- version: 19.2.4(react@19.2.4)
+ specifier: 'catalog:'
+ version: 19.2.6(react@19.2.6)
devDependencies:
'@tailwindcss/postcss':
specifier: ^4.3.0
version: 4.3.0
'@types/node':
- specifier: ^25.9.1
+ specifier: 'catalog:'
version: 25.9.1
'@types/react':
- specifier: ^19.2.15
+ specifier: 'catalog:'
version: 19.2.15
'@types/react-dom':
- specifier: ^19.2.3
+ specifier: 'catalog:'
version: 19.2.3(@types/react@19.2.15)
tailwindcss:
specifier: ^4.3.0
version: 4.3.0
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
examples/react:
dependencies:
@@ -303,26 +364,26 @@ importers:
specifier: workspace:*
version: link:../../permix
react:
- specifier: ^19.2.0
+ specifier: 'catalog:'
version: 19.2.6
react-dom:
- specifier: ^19.2.6
+ specifier: 'catalog:'
version: 19.2.6(react@19.2.6)
devDependencies:
'@types/react':
- specifier: ^19.2.15
+ specifier: 'catalog:'
version: 19.2.15
'@types/react-dom':
- specifier: ^19.2.3
+ specifier: 'catalog:'
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
- specifier: ^6.0.2
+ specifier: 'catalog:'
version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
vite:
- specifier: ^8.0.16
+ specifier: 'catalog:'
version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
examples/rebac:
@@ -332,14 +393,14 @@ importers:
version: link:../../permix
devDependencies:
'@types/node':
- specifier: ^25.9.1
+ specifier: 'catalog:'
version: 25.9.1
tsx:
- specifier: ^4.22.4
+ specifier: 'catalog:'
version: 4.22.4
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
examples/role-based:
dependencies:
@@ -347,26 +408,26 @@ importers:
specifier: workspace:*
version: link:../../permix
react:
- specifier: ^19.2.0
+ specifier: 'catalog:'
version: 19.2.6
react-dom:
- specifier: ^19.2.6
+ specifier: 'catalog:'
version: 19.2.6(react@19.2.6)
devDependencies:
'@types/react':
- specifier: ^19.2.15
+ specifier: 'catalog:'
version: 19.2.15
'@types/react-dom':
- specifier: ^19.2.3
+ specifier: 'catalog:'
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
- specifier: ^6.0.2
+ specifier: 'catalog:'
version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
vite:
- specifier: ^8.0.16
+ specifier: 'catalog:'
version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
examples/solid:
@@ -379,14 +440,14 @@ importers:
version: 1.9.13
devDependencies:
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
vite:
- specifier: ^8.0.16
+ specifier: 'catalog:'
version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
vite-plugin-solid:
specifier: ^2.11.12
- version: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
examples/svelte:
dependencies:
@@ -396,34 +457,34 @@ importers:
devDependencies:
'@sveltejs/adapter-auto':
specifier: ^7.0.1
- version: 7.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))
+ version: 7.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))
'@sveltejs/kit':
specifier: ^2.61.1
- version: 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@sveltejs/vite-plugin-svelte':
specifier: ^7.1.2
version: 7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@types/node':
- specifier: ^25.9.1
+ specifier: 'catalog:'
version: 25.9.1
svelte:
specifier: ^5.55.2
version: 5.56.0
svelte-check:
specifier: ^4.5.0
- version: 4.5.0(picomatch@4.0.7)(svelte@5.56.0)(typescript@6.0.3)
+ version: 4.5.0(picomatch@4.0.7)(svelte@5.56.0)(typescript@7.0.2)
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
vite:
- specifier: ^8.0.16
+ specifier: 'catalog:'
version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
examples/tanstack-start:
dependencies:
'@tailwindcss/vite':
specifier: ^4.1.18
- version: 4.3.0(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 4.3.0(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/react-devtools':
specifier: latest
version: 0.10.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(solid-js@1.9.13)
@@ -438,10 +499,10 @@ importers:
version: 1.167.1(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start':
specifier: latest
- version: 1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/router-plugin':
specifier: ^1.132.0
- version: 1.168.13(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 1.168.13(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
lucide-react:
specifier: ^0.545.0
version: 0.545.0(react@19.2.6)
@@ -449,10 +510,10 @@ importers:
specifier: workspace:*
version: link:../../permix
react:
- specifier: ^19.2.0
+ specifier: 'catalog:'
version: 19.2.6
react-dom:
- specifier: ^19.2.0
+ specifier: 'catalog:'
version: 19.2.6(react@19.2.6)
tailwindcss:
specifier: ^4.1.18
@@ -463,10 +524,10 @@ importers:
version: 0.5.19(tailwindcss@4.3.0)
'@tanstack/devtools-vite':
specifier: latest
- version: 0.8.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 0.8.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/router-cli':
specifier: ^1.132.0
- version: 1.167.17
+ version: 1.167.17(supports-color@7.2.0)
'@testing-library/dom':
specifier: ^10.4.1
version: 10.4.1
@@ -474,29 +535,29 @@ importers:
specifier: ^16.3.0
version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@types/node':
- specifier: ^22.10.2
- version: 22.19.20
+ specifier: 'catalog:'
+ version: 25.9.1
'@types/react':
- specifier: ^19.2.0
+ specifier: 'catalog:'
version: 19.2.15
'@types/react-dom':
- specifier: ^19.2.0
+ specifier: 'catalog:'
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
- specifier: ^6.0.1
- version: 6.0.2(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ specifier: 'catalog:'
+ version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
jsdom:
specifier: ^28.1.0
- version: 28.1.0(@noble/hashes@1.8.0)
+ version: 28.1.0(@noble/hashes@1.8.0)(supports-color@7.2.0)
typescript:
- specifier: ^6.0.2
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
vite:
- specifier: ^8.0.0
- version: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ specifier: 'catalog:'
+ version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
vitest:
- specifier: ^4.1.5
- version: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ specifier: 'catalog:'
+ version: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0)(supports-color@7.2.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
examples/vue:
dependencies:
@@ -505,23 +566,23 @@ importers:
version: link:../../permix
vue:
specifier: ^3.5.35
- version: 3.5.35(typescript@6.0.3)
+ version: 3.5.35(typescript@7.0.2)
devDependencies:
'@vitejs/plugin-vue':
specifier: ^6.0.7
- version: 6.0.7(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.35(typescript@6.0.3))
+ version: 6.0.7(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.35(typescript@7.0.2))
'@vue/tsconfig':
specifier: ^0.9.1
- version: 0.9.1(typescript@6.0.3)(vue@3.5.35(typescript@6.0.3))
+ version: 0.9.1(typescript@7.0.2)(vue@3.5.35(typescript@7.0.2))
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
vite:
- specifier: ^8.0.16
+ specifier: 'catalog:'
version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
vue-tsc:
specifier: ^3.3.3
- version: 3.3.3(typescript@6.0.3)
+ version: 3.3.3(typescript@7.0.2)
permix:
dependencies:
@@ -530,13 +591,13 @@ importers:
version: 1.14.4(crossws@0.4.5(srvx@0.11.16))(fastify@5.8.5)(ws@8.21.0)
'@trpc/server':
specifier: '>=11'
- version: 11.17.0(typescript@6.0.3)
+ version: 11.17.0(typescript@7.0.2)
elysia:
specifier: '>=1'
- version: 1.4.28(@sinclair/typebox@0.34.49)(exact-mirror@1.0.0)(file-type@22.0.1)(openapi-types@12.1.3)(typescript@6.0.3)
+ version: 1.4.28(@sinclair/typebox@0.34.49)(exact-mirror@1.0.0)(file-type@22.0.1(supports-color@7.2.0))(openapi-types@12.1.3)(typescript@7.0.2)
express:
specifier: '>=4'
- version: 5.2.1
+ version: 5.2.1(supports-color@7.2.0)
fastify:
specifier: '>=5'
version: 5.8.5
@@ -548,10 +609,7 @@ importers:
version: 4.12.23
next:
specifier: '>=14'
- version: 16.2.6(@babel/core@7.29.7)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- react:
- specifier: '>=18'
- version: 19.2.6
+ version: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
solid-js:
specifier: '>=1'
version: 1.9.13
@@ -561,7 +619,7 @@ importers:
version: 0.8.10(solid-js@1.9.13)
'@sveltejs/package':
specifier: ^2.5.7
- version: 2.5.7(svelte@5.56.0)(typescript@6.0.3)
+ version: 2.5.7(svelte@5.56.0)(typescript@7.0.2)
'@sveltejs/vite-plugin-svelte':
specifier: ^7.1.2
version: 7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -570,7 +628,7 @@ importers:
version: 0.0.42
'@tanstack/react-start':
specifier: ^1.168.18
- version: 1.168.18(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 1.168.18(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@testing-library/jest-dom':
specifier: ^6.9.1
version: 6.9.1
@@ -584,23 +642,23 @@ importers:
specifier: ^5.0.6
version: 5.0.6
'@types/node':
- specifier: ^25.9.1
+ specifier: 'catalog:'
version: 25.9.1
'@types/react':
- specifier: ^19.2.15
+ specifier: 'catalog:'
version: 19.2.15
'@types/supertest':
specifier: ^7.2.0
version: 7.2.0
'@vitejs/plugin-react':
- specifier: ^6.0.2
+ specifier: 'catalog:'
version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vitest/coverage-v8':
specifier: ^4.1.8
version: 4.1.8(vitest@4.1.8)
'@vue/test-utils':
specifier: ^2.4.10
- version: 2.4.10(@vue/compiler-dom@3.5.35)(@vue/server-renderer@3.5.35(vue@3.5.35(typescript@6.0.3)))(vue@3.5.35(typescript@6.0.3))
+ version: 2.4.10(@vue/compiler-dom@3.5.35)(@vue/server-renderer@3.5.35(vue@3.5.35(typescript@7.0.2)))(vue@3.5.35(typescript@7.0.2))
drizzle-orm:
specifier: 1.0.0-rc.3
version: 1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(effect@3.21.2)(zod@4.4.3)
@@ -610,33 +668,42 @@ importers:
happy-dom:
specifier: ^20.9.0
version: 20.9.0
+ react:
+ specifier: 'catalog:'
+ version: 19.2.6
react-dom:
- specifier: ^19.2.6
+ specifier: 'catalog:'
version: 19.2.6(react@19.2.6)
supertest:
specifier: ^7.2.2
- version: 7.2.2
+ version: 7.2.2(supports-color@7.2.0)
svelte:
specifier: ^5.56.0
version: 5.56.0
svelte-check:
specifier: ^4.5.0
- version: 4.5.0(picomatch@4.0.7)(svelte@5.56.0)(typescript@6.0.3)
+ version: 4.5.0(picomatch@4.0.7)(svelte@5.56.0)(typescript@7.0.2)
tsdown:
specifier: ^0.22.1
- version: 0.22.1(tsx@4.22.4)(typescript@6.0.3)
+ version: 0.22.1(tsx@4.22.4)(typescript@7.0.2)
typescript:
- specifier: ^6.0.3
- version: 6.0.3
+ specifier: 'catalog:'
+ version: 7.0.2
+ typescript59:
+ specifier: catalog:typescript-classic
+ version: typescript@5.9.3
+ typescript6:
+ specifier: catalog:typescript-classic
+ version: '@typescript/typescript6@6.0.2'
vite-plugin-solid:
specifier: ^2.11.12
- version: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
vitest:
- specifier: ^4.1.8
- version: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ specifier: 'catalog:'
+ version: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0)(supports-color@7.2.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
vue:
specifier: ^3.5.17
- version: 3.5.35(typescript@6.0.3)
+ version: 3.5.35(typescript@7.0.2)
zod:
specifier: ^4.4.3
version: 4.4.3
@@ -3087,33 +3154,33 @@ packages:
peerDependencies:
typescript: '>=5.7.2'
- '@turbo/darwin-64@2.9.16':
- resolution: {integrity: sha512-jLjApWTSNd7JZ5JaLYfelW1ytnGQOvB7ivl+2RD1xQvJTbi8I9gBjzcga7tDZVPyaxpl10YTfJt3BrYXR18KDw==}
+ '@turbo/darwin-64@2.10.12':
+ resolution: {integrity: sha512-9nKgKoF6ZOUsM+or0OtNf+TTJSfGvDNP7ZFv/ZGWVwOSCkumyctQiTeHwB4UNljHTnC41AqylgbunLDHoccNrA==}
cpu: [x64]
os: [darwin]
- '@turbo/darwin-arm64@2.9.16':
- resolution: {integrity: sha512-YPgrn+5HIGzrx0O2a631SV4MBQUe4W/DafMFUuBVgaU32PW9/OTT0ehviF0QSxTXuRJlHvW2eUTemddF5/spmw==}
+ '@turbo/darwin-arm64@2.10.12':
+ resolution: {integrity: sha512-H4Elb1jqTZVeIC9bbcNwjSzemZ6RegoTOVHeuV5Osirt2Z8UguTyisMEkvZjPVZgMeN9J4ERZBFad40tFnkb7w==}
cpu: [arm64]
os: [darwin]
- '@turbo/linux-64@2.9.16':
- resolution: {integrity: sha512-vAEf1H6l26lTpl9FJ/peQo1NUB8RC0sbEJJz5mPcUhHA2bPDup2x3CZPgo/bH8S4cUcBLm4FN3UHd5iUO2RAew==}
+ '@turbo/linux-64@2.10.12':
+ resolution: {integrity: sha512-lr7KIotukvjZwEXiFSYAeOH3BWzjFVBbSzTbv0fuGFsNukYyH0+g1hB5ecqnJkgkYU+KHEMG1edOhnjiKON1wQ==}
cpu: [x64]
- os: [linux]
+ os: [android, linux]
- '@turbo/linux-arm64@2.9.16':
- resolution: {integrity: sha512-xDBLR2PZg4BrQOchfG6svgpv5FCNJ2TOtT2psLdEJcdKo1BH+pnPs9Xj6pvUjgfkHbuvBOfeE4R6tvxMoQKDHQ==}
+ '@turbo/linux-arm64@2.10.12':
+ resolution: {integrity: sha512-f0pZDTtvzB5SuNwuXBaKbZHUCMCukgc8nMlHEuvLmj91Fzec+MEbr3cAvGNor5htEDqZnO6Lxt9N/GPI/77oGA==}
cpu: [arm64]
- os: [linux]
+ os: [android, linux]
- '@turbo/windows-64@2.9.16':
- resolution: {integrity: sha512-NBAJnaUiGdgkSzQwUIdOvkCkcpTSu58G/sBGa0mvBtzfvFOOgrQwepKOOQ8cp6sWM6OcKDNFj2p1dsZA1OWjPg==}
+ '@turbo/windows-64@2.10.12':
+ resolution: {integrity: sha512-SDOueJRjS/QcykWf2KCRtTLmIl5YMKsLbXkXQGhDwcTXvKXZiS5ih5lBl/gkwZIpYFjqA/rAlfMzlAFcVHNe0g==}
cpu: [x64]
os: [win32]
- '@turbo/windows-arm64@2.9.16':
- resolution: {integrity: sha512-Y7SJppD0Z8wjO3Ec0ZGd9KQ4Yv0BMnA8CIowj5Vp+OEVsosXDG2weK6/t1RRLfJmc2Ozrnd6y4DOgQys+mn3WQ==}
+ '@turbo/windows-arm64@2.10.12':
+ resolution: {integrity: sha512-0i0mVUa4kKk+/B3RwEwPMf9CB+T7ul56hn5FFHNA4VUNTOoLBEd6aNf3FaKfCatDNZ6cicCEf6if9QUTVyzzcA==}
cpu: [arm64]
os: [win32]
@@ -3288,12 +3355,6 @@ packages:
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
- '@types/node@22.19.20':
- resolution: {integrity: sha512-6tELRwSDYWW9EdZhbeZmYGZ1/7Djkt+Ah3/ScEYT9cDord7UJzasR/4D3VONg9tQI5CDp+/CZC1AXj2pCFOvpw==}
-
- '@types/node@24.10.0':
- resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==}
-
'@types/node@25.9.1':
resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==}
@@ -3341,6 +3402,130 @@ packages:
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
+ '@typescript/typescript-aix-ppc64@7.0.2':
+ resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@typescript/typescript-darwin-arm64@7.0.2':
+ resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@typescript/typescript-darwin-x64@7.0.2':
+ resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@typescript/typescript-freebsd-arm64@7.0.2':
+ resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@typescript/typescript-freebsd-x64@7.0.2':
+ resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@typescript/typescript-linux-arm64@7.0.2':
+ resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@typescript/typescript-linux-arm@7.0.2':
+ resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@typescript/typescript-linux-loong64@7.0.2':
+ resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@typescript/typescript-linux-mips64el@7.0.2':
+ resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@typescript/typescript-linux-ppc64@7.0.2':
+ resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@typescript/typescript-linux-riscv64@7.0.2':
+ resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@typescript/typescript-linux-s390x@7.0.2':
+ resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==}
+ engines: {node: '>=16.20.0'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@typescript/typescript-linux-x64@7.0.2':
+ resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@typescript/typescript-netbsd-arm64@7.0.2':
+ resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@typescript/typescript-netbsd-x64@7.0.2':
+ resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@typescript/typescript-openbsd-arm64@7.0.2':
+ resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@typescript/typescript-openbsd-x64@7.0.2':
+ resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@typescript/typescript-sunos-x64@7.0.2':
+ resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@typescript/typescript-win32-arm64@7.0.2':
+ resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@typescript/typescript-win32-x64@7.0.2':
+ resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [win32]
+
+ '@typescript/typescript6@6.0.2':
+ resolution: {integrity: sha512-mbCddXd+jm7hfx7w2YU64/Av4/NqqeG3GoRZgxPcgoTxYjhrcfJRw9ULch71SS4G+Q3bOXFhRvPqjguN0Hyp5w==}
+ hasBin: true
+
'@typescript/vfs@1.6.4':
resolution: {integrity: sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ==}
peerDependencies:
@@ -3447,6 +3632,11 @@ packages:
'@volar/typescript@2.4.28':
resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
'@vue/compiler-core@3.5.35':
resolution: {integrity: sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==}
@@ -5194,9 +5384,6 @@ packages:
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
- magicast@0.5.3:
- resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==}
-
magicast@0.5.4:
resolution: {integrity: sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==}
@@ -5854,11 +6041,6 @@ packages:
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
engines: {node: '>= 0.10'}
- react-dom@19.2.4:
- resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
- peerDependencies:
- react: ^19.2.4
-
react-dom@19.2.6:
resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==}
peerDependencies:
@@ -5897,10 +6079,6 @@ packages:
'@types/react':
optional: true
- react@19.2.4:
- resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
- engines: {node: '>=0.10.0'}
-
react@19.2.6:
resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==}
engines: {node: '>=0.10.0'}
@@ -6457,8 +6635,8 @@ packages:
engines: {node: '>=18.0.0'}
hasBin: true
- turbo@2.9.16:
- resolution: {integrity: sha512-NqgRQy6j6dPYcdSdv0q1g9QsZg7SWg87RERM8otw/1AtKU2yTFVClOM7cbwKzOonZr/Ek1blTBucw64L9H0Bwg==}
+ turbo@2.10.12:
+ resolution: {integrity: sha512-AswgMPnpOoaVZHrrSBejETzEbuIA69OVGwfkHwfrY0A23VjWXBANzgq9+OymWOHAIArB7D1+1z498WY8fGg1Jw==}
hasBin: true
twoslash-protocol@0.3.8:
@@ -6477,11 +6655,21 @@ packages:
resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==}
engines: {node: '>= 18'}
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
typescript@6.0.3:
resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
engines: {node: '>=14.17'}
hasBin: true
+ typescript@7.0.2:
+ resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==}
+ engines: {node: '>=16.20.0'}
+ hasBin: true
+
ufo@1.6.4:
resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==}
@@ -6507,12 +6695,6 @@ packages:
unconfig@7.5.0:
resolution: {integrity: sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==}
- undici-types@6.21.0:
- resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
-
- undici-types@7.16.0:
- resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
-
undici-types@7.24.6:
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
@@ -6948,7 +7130,7 @@ snapshots:
'@antfu/install-pkg@1.1.0':
dependencies:
package-manager-detector: 1.6.0
- tinyexec: 1.2.3
+ tinyexec: 1.3.0
'@antfu/ni@30.1.0':
dependencies:
@@ -6991,20 +7173,20 @@ snapshots:
'@babel/compat-data@7.29.7': {}
- '@babel/core@7.29.7':
+ '@babel/core@7.29.7(supports-color@7.2.0)':
dependencies:
'@babel/code-frame': 7.29.7
'@babel/generator': 7.29.7
'@babel/helper-compilation-targets': 7.29.7
- '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
+ '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)
'@babel/helpers': 7.29.7
'@babel/parser': 7.29.7
'@babel/template': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/traverse': 7.29.7(supports-color@7.2.0)
'@babel/types': 7.29.7
'@jridgewell/remapping': 2.3.5
convert-source-map: 2.0.0
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -7042,19 +7224,19 @@ snapshots:
dependencies:
'@babel/types': 7.29.7
- '@babel/helper-module-imports@7.29.7':
+ '@babel/helper-module-imports@7.29.7(supports-color@7.2.0)':
dependencies:
- '@babel/traverse': 7.29.7
+ '@babel/traverse': 7.29.7(supports-color@7.2.0)
'@babel/types': 7.29.7
transitivePeerDependencies:
- supports-color
- '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
+ '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-module-imports': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
+ '@babel/helper-module-imports': 7.29.7(supports-color@7.2.0)
'@babel/helper-validator-identifier': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/traverse': 7.29.7(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
@@ -7083,14 +7265,14 @@ snapshots:
dependencies:
'@babel/types': 8.0.0-rc.6
- '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))':
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
'@babel/helper-plugin-utils': 7.29.7
- '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))':
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
'@babel/helper-plugin-utils': 7.29.7
'@babel/runtime@7.29.7': {}
@@ -7101,7 +7283,7 @@ snapshots:
'@babel/parser': 7.29.7
'@babel/types': 7.29.7
- '@babel/traverse@7.29.7':
+ '@babel/traverse@7.29.7(supports-color@7.2.0)':
dependencies:
'@babel/code-frame': 7.29.7
'@babel/generator': 7.29.7
@@ -7109,7 +7291,7 @@ snapshots:
'@babel/parser': 7.29.7
'@babel/template': 7.29.7
'@babel/types': 7.29.7
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
@@ -7449,7 +7631,7 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
- '@mdx-js/mdx@3.1.1':
+ '@mdx-js/mdx@3.1.1(supports-color@7.2.0)':
dependencies:
'@types/estree': 1.0.9
'@types/estree-jsx': 1.0.5
@@ -7461,14 +7643,14 @@ snapshots:
estree-util-is-identifier-name: 3.0.0
estree-util-scope: 1.0.0
estree-walker: 3.0.3
- hast-util-to-jsx-runtime: 2.3.6
+ hast-util-to-jsx-runtime: 2.3.6(supports-color@7.2.0)
markdown-extensions: 2.0.0
recma-build-jsx: 1.0.0
recma-jsx: 1.0.1(acorn@8.16.0)
recma-stringify: 1.0.0
- rehype-recma: 1.0.0
- remark-mdx: 3.1.1
- remark-parse: 11.0.0
+ rehype-recma: 1.0.0(supports-color@7.2.0)
+ remark-mdx: 3.1.1(supports-color@7.2.0)
+ remark-parse: 11.0.0(supports-color@7.2.0)
remark-rehype: 11.1.2
source-map: 0.7.6
unified: 11.0.5
@@ -8312,12 +8494,12 @@ snapshots:
dependencies:
'@shikijs/types': 4.1.0
- '@shikijs/twoslash@4.1.0(typescript@6.0.3)':
+ '@shikijs/twoslash@4.1.0(supports-color@7.2.0)(typescript@7.0.2)':
dependencies:
'@shikijs/core': 4.1.0
'@shikijs/types': 4.1.0
- twoslash: 0.3.8(typescript@6.0.3)
- typescript: 6.0.3
+ twoslash: 0.3.8(supports-color@7.2.0)(typescript@7.0.2)
+ typescript: 7.0.2
transitivePeerDependencies:
- supports-color
@@ -8377,32 +8559,11 @@ snapshots:
dependencies:
acorn: 8.16.0
- '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))':
+ '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))':
dependencies:
- '@sveltejs/kit': 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@sveltejs/kit': 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- '@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
- dependencies:
- '@standard-schema/spec': 1.1.0
- '@sveltejs/acorn-typescript': 1.0.10(acorn@8.16.0)
- '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- '@types/cookie': 0.6.0
- acorn: 8.16.0
- cookie: 0.6.0
- devalue: 5.8.1
- esm-env: 1.2.2
- kleur: 4.1.5
- magic-string: 0.30.21
- mrmime: 2.0.1
- set-cookie-parser: 3.1.0
- sirv: 3.0.2
- svelte: 5.56.0
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- optionalDependencies:
- typescript: 6.0.3
- optional: true
-
- '@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@standard-schema/spec': 1.1.0
'@sveltejs/acorn-typescript': 1.0.10(acorn@8.16.0)
@@ -8420,29 +8581,19 @@ snapshots:
svelte: 5.56.0
vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
optionalDependencies:
- typescript: 6.0.3
+ typescript: 7.0.2
- '@sveltejs/package@2.5.7(svelte@5.56.0)(typescript@6.0.3)':
+ '@sveltejs/package@2.5.7(svelte@5.56.0)(typescript@7.0.2)':
dependencies:
chokidar: 5.0.0
kleur: 4.1.5
sade: 1.8.1
semver: 7.8.1
svelte: 5.56.0
- svelte2tsx: 0.7.55(svelte@5.56.0)(typescript@6.0.3)
+ svelte2tsx: 0.7.55(svelte@5.56.0)(typescript@7.0.2)
transitivePeerDependencies:
- typescript
- '@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
- dependencies:
- deepmerge: 4.3.1
- magic-string: 0.30.21
- obug: 2.1.1
- svelte: 5.56.0
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitefu: 1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- optional: true
-
'@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
deepmerge: 4.3.1
@@ -8530,19 +8681,12 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 4.3.0
- '@tailwindcss/vite@4.3.0(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tailwindcss/vite@4.3.0(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tailwindcss/node': 4.3.0
'@tailwindcss/oxide': 4.3.0
tailwindcss: 4.3.0
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
-
- '@tailwindcss/vite@4.3.0(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
- dependencies:
- '@tailwindcss/node': 4.3.0
- '@tailwindcss/oxide': 4.3.0
- tailwindcss: 4.3.0
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
'@tanstack/devtools-bundler-core@0.1.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
dependencies:
@@ -8581,13 +8725,13 @@ snapshots:
transitivePeerDependencies:
- csstype
- '@tanstack/devtools-vite@0.8.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/devtools-vite@0.8.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/devtools-bundler-core': 0.1.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
'@tanstack/devtools-client': 0.0.8
'@tanstack/devtools-event-bus': 0.4.3
chalk: 5.6.2
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -8727,15 +8871,15 @@ snapshots:
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- '@tanstack/react-start-rsc@0.1.17(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start-rsc@0.1.17(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-server': 1.167.13(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/router-core': 1.171.8
- '@tanstack/router-utils': 1.162.1
+ '@tanstack/router-utils': 1.162.1(supports-color@7.2.0)
'@tanstack/start-client-core': 1.170.6
'@tanstack/start-fn-stubs': 1.162.0
- '@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-server-core': 1.169.8(crossws@0.4.5(srvx@0.11.16))
'@tanstack/start-storage-context': 1.167.10
pathe: 2.0.3
@@ -8749,14 +8893,14 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/react-start-rsc@0.1.23(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start-rsc@0.1.23(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/router-core': 1.171.13
- '@tanstack/router-utils': 1.162.2
+ '@tanstack/router-utils': 1.162.2(supports-color@7.2.0)
'@tanstack/start-client-core': 1.170.11
'@tanstack/start-fn-stubs': 1.162.0
- '@tanstack/start-plugin-core': 1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-server-core': 1.169.13(crossws@0.4.5(srvx@0.11.16))
'@tanstack/start-storage-context': 1.167.15
pathe: 2.0.3
@@ -8770,14 +8914,14 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/react-start-rsc@0.1.48(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start-rsc@0.1.48(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/router-core': 1.171.27
- '@tanstack/router-utils': 1.162.2
+ '@tanstack/router-utils': 1.162.2(supports-color@7.2.0)
'@tanstack/start-client-core': 1.170.27
'@tanstack/start-fn-stubs': 1.162.0
- '@tanstack/start-plugin-core': 1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-storage-context': 1.167.29
pathe: 2.0.3
react: 19.2.6
@@ -8822,15 +8966,15 @@ snapshots:
transitivePeerDependencies:
- crossws
- '@tanstack/react-start@1.168.18(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start@1.168.18(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-client': 1.168.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/react-start-rsc': 0.1.17(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/react-start-rsc': 0.1.17(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/react-start-server': 1.167.13(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/router-utils': 1.162.1
+ '@tanstack/router-utils': 1.162.1(supports-color@7.2.0)
'@tanstack/start-client-core': 1.170.6
- '@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-server-core': 1.169.8(crossws@0.4.5(srvx@0.11.16))
pathe: 2.0.3
react: 19.2.6
@@ -8845,21 +8989,21 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/react-start@1.168.24(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start@1.168.24(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-client': 1.168.12(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/react-start-rsc': 0.1.23(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/react-start-rsc': 0.1.23(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/react-start-server': 1.167.18(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/router-utils': 1.162.2
+ '@tanstack/router-utils': 1.162.2(supports-color@7.2.0)
'@tanstack/start-client-core': 1.170.11
- '@tanstack/start-plugin-core': 1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-server-core': 1.169.13(crossws@0.4.5(srvx@0.11.16))
pathe: 2.0.3
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
optionalDependencies:
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@rspack/core'
- crossws
@@ -8868,21 +9012,21 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/react-start@1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start@1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-client': 1.168.30(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/react-start-rsc': 0.1.48(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/react-start-rsc': 0.1.48(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/react-start-server': 1.167.37(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/router-utils': 1.162.2
+ '@tanstack/router-utils': 1.162.2(supports-color@7.2.0)
'@tanstack/start-client-core': 1.170.27
- '@tanstack/start-plugin-core': 1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-server-core': 1.169.31(crossws@0.4.5(srvx@0.11.16))
pathe: 2.0.3
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
optionalDependencies:
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@rspack/core'
- crossws
@@ -8898,9 +9042,9 @@ snapshots:
react-dom: 19.2.6(react@19.2.6)
use-sync-external-store: 1.6.0(react@19.2.6)
- '@tanstack/router-cli@1.167.17':
+ '@tanstack/router-cli@1.167.17(supports-color@7.2.0)':
dependencies:
- '@tanstack/router-generator': 1.167.17
+ '@tanstack/router-generator': 1.167.17(supports-color@7.2.0)
chokidar: 5.0.0
yargs: 17.7.2
transitivePeerDependencies:
@@ -8943,11 +9087,11 @@ snapshots:
optionalDependencies:
csstype: 3.2.3
- '@tanstack/router-generator@1.167.12':
+ '@tanstack/router-generator@1.167.12(supports-color@7.2.0)':
dependencies:
'@babel/types': 7.29.7
'@tanstack/router-core': 1.171.8
- '@tanstack/router-utils': 1.162.1
+ '@tanstack/router-utils': 1.162.1(supports-color@7.2.0)
'@tanstack/virtual-file-routes': 1.162.0
jiti: 2.7.0
magic-string: 0.30.21
@@ -8956,11 +9100,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tanstack/router-generator@1.167.17':
+ '@tanstack/router-generator@1.167.17(supports-color@7.2.0)':
dependencies:
'@babel/types': 7.29.7
'@tanstack/router-core': 1.171.13
- '@tanstack/router-utils': 1.162.2
+ '@tanstack/router-utils': 1.162.2(supports-color@7.2.0)
'@tanstack/virtual-file-routes': 1.162.0
jiti: 2.7.0
magic-string: 0.30.21
@@ -8969,11 +9113,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tanstack/router-generator@1.167.33':
+ '@tanstack/router-generator@1.167.33(supports-color@7.2.0)':
dependencies:
'@babel/types': 7.29.7
'@tanstack/router-core': 1.171.27
- '@tanstack/router-utils': 1.162.2
+ '@tanstack/router-utils': 1.162.2(supports-color@7.2.0)
'@tanstack/virtual-file-routes': 1.162.0
jiti: 2.7.0
magic-string: 0.30.21
@@ -8982,17 +9126,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
- '@babel/core': 7.29.7
- '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/core': 7.29.7(supports-color@7.2.0)
+ '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))
+ '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))
'@babel/template': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/traverse': 7.29.7(supports-color@7.2.0)
'@babel/types': 7.29.7
'@tanstack/router-core': 1.171.8
- '@tanstack/router-generator': 1.167.12
- '@tanstack/router-utils': 1.162.1
+ '@tanstack/router-generator': 1.167.12(supports-color@7.2.0)
+ '@tanstack/router-utils': 1.162.1(supports-color@7.2.0)
'@tanstack/virtual-file-routes': 1.162.0
chokidar: 5.0.0
unplugin: 3.0.0
@@ -9000,65 +9144,65 @@ snapshots:
optionalDependencies:
'@tanstack/react-router': 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
transitivePeerDependencies:
- supports-color
- '@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
- '@babel/core': 7.29.7
- '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/core': 7.29.7(supports-color@7.2.0)
+ '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))
+ '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))
'@babel/template': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/traverse': 7.29.7(supports-color@7.2.0)
'@babel/types': 7.29.7
'@tanstack/router-core': 1.171.8
- '@tanstack/router-generator': 1.167.12
- '@tanstack/router-utils': 1.162.1
+ '@tanstack/router-generator': 1.167.12(supports-color@7.2.0)
+ '@tanstack/router-utils': 1.162.1(supports-color@7.2.0)
'@tanstack/virtual-file-routes': 1.162.0
chokidar: 5.0.0
unplugin: 3.0.0
zod: 4.4.3
optionalDependencies:
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
transitivePeerDependencies:
- supports-color
- '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
'@babel/template': 7.29.7
'@babel/types': 7.29.7
'@tanstack/router-core': 1.171.13
- '@tanstack/router-generator': 1.167.17
- '@tanstack/router-utils': 1.162.2
+ '@tanstack/router-generator': 1.167.17(supports-color@7.2.0)
+ '@tanstack/router-utils': 1.162.2(supports-color@7.2.0)
chokidar: 5.0.0
unplugin: 3.0.0
zod: 4.4.3
optionalDependencies:
'@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
transitivePeerDependencies:
- supports-color
- '@tanstack/router-plugin@1.168.35(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/router-plugin@1.168.35(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
'@babel/template': 7.29.7
'@babel/types': 7.29.7
'@tanstack/router-core': 1.171.27
- '@tanstack/router-generator': 1.167.33
- '@tanstack/router-utils': 1.162.2
+ '@tanstack/router-generator': 1.167.33(supports-color@7.2.0)
+ '@tanstack/router-utils': 1.162.2(supports-color@7.2.0)
chokidar: 5.0.0
unplugin: 3.0.0
zod: 4.4.3
optionalDependencies:
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
transitivePeerDependencies:
- supports-color
@@ -9067,27 +9211,27 @@ snapshots:
'@tanstack/query-core': 5.101.0
'@tanstack/router-core': 1.171.27
- '@tanstack/router-utils@1.162.1':
+ '@tanstack/router-utils@1.162.1(supports-color@7.2.0)':
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
'@babel/generator': 7.29.7
'@babel/parser': 7.29.7
'@babel/types': 7.29.7
ansis: 4.3.0
- babel-dead-code-elimination: 1.0.12
+ babel-dead-code-elimination: 1.0.12(supports-color@7.2.0)
diff: 8.0.4
pathe: 2.0.3
tinyglobby: 0.2.17
transitivePeerDependencies:
- supports-color
- '@tanstack/router-utils@1.162.2':
+ '@tanstack/router-utils@1.162.2(supports-color@7.2.0)':
dependencies:
'@babel/generator': 7.29.7
'@babel/parser': 7.29.7
'@babel/types': 7.29.7
ansis: 4.3.0
- babel-dead-code-elimination: 1.0.12
+ babel-dead-code-elimination: 1.0.12(supports-color@7.2.0)
diff: 8.0.4
pathe: 2.0.3
tinyglobby: 0.2.17
@@ -9117,22 +9261,22 @@ snapshots:
'@tanstack/start-fn-stubs@1.162.0': {}
- '@tanstack/start-plugin-core@1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/start-plugin-core@1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
'@babel/types': 7.29.7
'@rolldown/pluginutils': 1.0.1
'@tanstack/router-core': 1.171.8
- '@tanstack/router-generator': 1.167.12
- '@tanstack/router-plugin': 1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- '@tanstack/router-utils': 1.162.1
+ '@tanstack/router-generator': 1.167.12(supports-color@7.2.0)
+ '@tanstack/router-plugin': 1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/router-utils': 1.162.1(supports-color@7.2.0)
'@tanstack/start-client-core': 1.170.6
'@tanstack/start-server-core': 1.169.8(crossws@0.4.5(srvx@0.11.16))
exsolve: 1.0.8
lightningcss: 1.32.0
pathe: 2.0.3
- picomatch: 4.0.4
+ picomatch: 4.0.7
seroval: 1.5.4
source-map: 0.7.6
srvx: 0.11.16
@@ -9150,15 +9294,15 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/start-plugin-core@1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/start-plugin-core@1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
'@babel/types': 7.29.7
'@tanstack/router-core': 1.171.13
- '@tanstack/router-generator': 1.167.17
- '@tanstack/router-plugin': 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- '@tanstack/router-utils': 1.162.2
+ '@tanstack/router-generator': 1.167.17(supports-color@7.2.0)
+ '@tanstack/router-plugin': 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/router-utils': 1.162.2(supports-color@7.2.0)
'@tanstack/start-server-core': 1.169.13(crossws@0.4.5(srvx@0.11.16))
exsolve: 1.0.8
lightningcss: 1.32.0
@@ -9169,11 +9313,11 @@ snapshots:
srvx: 0.11.16
tinyglobby: 0.2.17
ufo: 1.6.4
- vitefu: 1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitefu: 1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
xmlbuilder2: 4.0.3
zod: 4.4.3
optionalDependencies:
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@tanstack/react-router'
- crossws
@@ -9181,30 +9325,30 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/start-plugin-core@1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/start-plugin-core@1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
'@babel/types': 7.29.7
'@tanstack/router-core': 1.171.27
- '@tanstack/router-generator': 1.167.33
- '@tanstack/router-plugin': 1.168.35(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- '@tanstack/router-utils': 1.162.2
+ '@tanstack/router-generator': 1.167.33(supports-color@7.2.0)
+ '@tanstack/router-plugin': 1.168.35(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/router-utils': 1.162.2(supports-color@7.2.0)
'@tanstack/start-server-core': 1.169.31(crossws@0.4.5(srvx@0.11.16))
exsolve: 1.0.8
lightningcss: 1.32.0
pathe: 2.0.3
- picomatch: 4.0.4
+ picomatch: 4.0.7
seroval: 1.6.4
source-map: 0.7.6
srvx: 0.11.16
tinyglobby: 0.2.17
ufo: 1.6.4
- vitefu: 1.1.3(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitefu: 1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
xmlbuilder2: 4.0.3
zod: 4.4.3
optionalDependencies:
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@tanstack/react-router'
- crossws
@@ -9305,42 +9449,42 @@ snapshots:
svelte: 5.56.0
optionalDependencies:
vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0)(supports-color@7.2.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- '@tokenizer/inflate@0.4.1':
+ '@tokenizer/inflate@0.4.1(supports-color@7.2.0)':
dependencies:
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
token-types: 6.1.2
transitivePeerDependencies:
- supports-color
'@tokenizer/token@0.3.0': {}
- '@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@6.0.3))(typescript@6.0.3)':
+ '@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@7.0.2))(typescript@7.0.2)':
dependencies:
- '@trpc/server': 11.17.0(typescript@6.0.3)
- typescript: 6.0.3
+ '@trpc/server': 11.17.0(typescript@7.0.2)
+ typescript: 7.0.2
- '@trpc/server@11.17.0(typescript@6.0.3)':
+ '@trpc/server@11.17.0(typescript@7.0.2)':
dependencies:
- typescript: 6.0.3
+ typescript: 7.0.2
- '@turbo/darwin-64@2.9.16':
+ '@turbo/darwin-64@2.10.12':
optional: true
- '@turbo/darwin-arm64@2.9.16':
+ '@turbo/darwin-arm64@2.10.12':
optional: true
- '@turbo/linux-64@2.9.16':
+ '@turbo/linux-64@2.10.12':
optional: true
- '@turbo/linux-arm64@2.9.16':
+ '@turbo/linux-arm64@2.10.12':
optional: true
- '@turbo/windows-64@2.9.16':
+ '@turbo/windows-64@2.10.12':
optional: true
- '@turbo/windows-arm64@2.9.16':
+ '@turbo/windows-arm64@2.10.12':
optional: true
'@tybys/wasm-util@0.10.2':
@@ -9555,14 +9699,6 @@ snapshots:
'@types/ms@2.1.0': {}
- '@types/node@22.19.20':
- dependencies:
- undici-types: 6.21.0
-
- '@types/node@24.10.0':
- dependencies:
- undici-types: 7.16.0
-
'@types/node@25.9.1':
dependencies:
undici-types: 7.24.6
@@ -9618,10 +9754,74 @@ snapshots:
dependencies:
'@types/node': 25.9.1
- '@typescript/vfs@1.6.4(typescript@6.0.3)':
+ '@typescript/typescript-aix-ppc64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-darwin-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-darwin-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-freebsd-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-freebsd-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-arm@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-loong64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-mips64el@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-ppc64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-riscv64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-s390x@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-netbsd-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-netbsd-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-openbsd-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-openbsd-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-sunos-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-win32-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-win32-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript6@6.0.2':
+ dependencies:
+ '@typescript/old': typescript@6.0.3
+
+ '@typescript/vfs@1.6.4(supports-color@7.2.0)(typescript@7.0.2)':
dependencies:
- debug: 4.4.3
- typescript: 6.0.3
+ debug: 4.4.3(supports-color@7.2.0)
+ typescript: 7.0.2
transitivePeerDependencies:
- supports-color
@@ -9632,34 +9832,24 @@ snapshots:
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
- '@vercel/analytics@2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@6.0.3))':
+ '@vercel/analytics@2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@7.0.2))':
optionalDependencies:
- '@sveltejs/kit': 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- next: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@sveltejs/kit': 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ next: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
svelte: 5.56.0
- vue: 3.5.35(typescript@6.0.3)
-
- '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
- dependencies:
- '@rolldown/pluginutils': 1.0.1
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
-
- '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
- dependencies:
- '@rolldown/pluginutils': 1.0.1
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vue: 3.5.35(typescript@7.0.2)
'@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@rolldown/pluginutils': 1.0.1
vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- '@vitejs/plugin-vue@6.0.7(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.35(typescript@6.0.3))':
+ '@vitejs/plugin-vue@6.0.7(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.35(typescript@7.0.2))':
dependencies:
'@rolldown/pluginutils': 1.0.1
vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vue: 3.5.35(typescript@6.0.3)
+ vue: 3.5.35(typescript@7.0.2)
'@vitest/coverage-v8@4.1.8(vitest@4.1.8)':
dependencies:
@@ -9669,11 +9859,11 @@ snapshots:
istanbul-lib-coverage: 3.2.2
istanbul-lib-report: 3.0.1
istanbul-reports: 3.2.0
- magicast: 0.5.3
+ magicast: 0.5.4
obug: 2.1.1
std-env: 4.1.0
tinyrainbow: 3.1.0
- vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0)(supports-color@7.2.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vitest/expect@4.1.8':
dependencies:
@@ -9684,14 +9874,6 @@ snapshots:
chai: 6.2.2
tinyrainbow: 3.1.0
- '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
- dependencies:
- '@vitest/spy': 4.1.8
- estree-walker: 3.0.3
- magic-string: 0.30.21
- optionalDependencies:
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
-
'@vitest/mocker@4.1.8(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@vitest/spy': 4.1.8
@@ -9730,11 +9912,13 @@ snapshots:
'@volar/source-map@2.4.28': {}
- '@volar/typescript@2.4.28':
+ '@volar/typescript@2.4.28(typescript@7.0.2)':
dependencies:
'@volar/language-core': 2.4.28
path-browserify: 1.0.1
vscode-uri: 3.1.0
+ optionalDependencies:
+ typescript: 7.0.2
'@vue/compiler-core@3.5.35':
dependencies:
@@ -9774,7 +9958,7 @@ snapshots:
alien-signals: 3.2.1
muggle-string: 0.4.1
path-browserify: 1.0.1
- picomatch: 4.0.4
+ picomatch: 4.0.7
'@vue/reactivity@3.5.35':
dependencies:
@@ -9792,27 +9976,27 @@ snapshots:
'@vue/shared': 3.5.35
csstype: 3.2.3
- '@vue/server-renderer@3.5.35(vue@3.5.35(typescript@6.0.3))':
+ '@vue/server-renderer@3.5.35(vue@3.5.35(typescript@7.0.2))':
dependencies:
'@vue/compiler-ssr': 3.5.35
'@vue/shared': 3.5.35
- vue: 3.5.35(typescript@6.0.3)
+ vue: 3.5.35(typescript@7.0.2)
'@vue/shared@3.5.35': {}
- '@vue/test-utils@2.4.10(@vue/compiler-dom@3.5.35)(@vue/server-renderer@3.5.35(vue@3.5.35(typescript@6.0.3)))(vue@3.5.35(typescript@6.0.3))':
+ '@vue/test-utils@2.4.10(@vue/compiler-dom@3.5.35)(@vue/server-renderer@3.5.35(vue@3.5.35(typescript@7.0.2)))(vue@3.5.35(typescript@7.0.2))':
dependencies:
'@vue/compiler-dom': 3.5.35
js-beautify: 1.15.4
- vue: 3.5.35(typescript@6.0.3)
+ vue: 3.5.35(typescript@7.0.2)
vue-component-type-helpers: 3.3.3
optionalDependencies:
- '@vue/server-renderer': 3.5.35(vue@3.5.35(typescript@6.0.3))
+ '@vue/server-renderer': 3.5.35(vue@3.5.35(typescript@7.0.2))
- '@vue/tsconfig@0.9.1(typescript@6.0.3)(vue@3.5.35(typescript@6.0.3))':
+ '@vue/tsconfig@0.9.1(typescript@7.0.2)(vue@3.5.35(typescript@7.0.2))':
optionalDependencies:
- typescript: 6.0.3
- vue: 3.5.35(typescript@6.0.3)
+ typescript: 7.0.2
+ vue: 3.5.35(typescript@7.0.2)
abbrev@2.0.0: {}
@@ -9905,28 +10089,28 @@ snapshots:
axobject-query@4.1.0: {}
- babel-dead-code-elimination@1.0.12:
+ babel-dead-code-elimination@1.0.12(supports-color@7.2.0):
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
'@babel/parser': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/traverse': 7.29.7(supports-color@7.2.0)
'@babel/types': 7.29.7
transitivePeerDependencies:
- supports-color
- babel-plugin-jsx-dom-expressions@0.40.7(@babel/core@7.29.7):
+ babel-plugin-jsx-dom-expressions@0.40.7(@babel/core@7.29.7(supports-color@7.2.0)):
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
'@babel/helper-module-imports': 7.18.6
- '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))
'@babel/types': 7.29.7
html-entities: 2.3.3
parse5: 7.3.0
- babel-preset-solid@1.9.12(@babel/core@7.29.7)(solid-js@1.9.13):
+ babel-preset-solid@1.9.12(@babel/core@7.29.7(supports-color@7.2.0))(solid-js@1.9.13):
dependencies:
- '@babel/core': 7.29.7
- babel-plugin-jsx-dom-expressions: 0.40.7(@babel/core@7.29.7)
+ '@babel/core': 7.29.7(supports-color@7.2.0)
+ babel-plugin-jsx-dom-expressions: 0.40.7(@babel/core@7.29.7(supports-color@7.2.0))
optionalDependencies:
solid-js: 1.9.13
@@ -9942,11 +10126,11 @@ snapshots:
birpc@4.0.0: {}
- body-parser@2.2.2:
+ body-parser@2.2.2(supports-color@7.2.0):
dependencies:
bytes: 3.1.2
content-type: 1.0.5
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
http-errors: 2.0.1
iconv-lite: 0.7.2
on-finished: 2.4.1
@@ -10330,9 +10514,11 @@ snapshots:
optionalDependencies:
drizzle-orm: 1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(effect@3.21.2)(zod@4.4.3)
- debug@4.4.3:
+ debug@4.4.3(supports-color@7.2.0):
dependencies:
ms: 2.1.3
+ optionalDependencies:
+ supports-color: 7.2.0
decimal.js@10.6.0: {}
@@ -10416,17 +10602,17 @@ snapshots:
electron-to-chromium@1.5.364: {}
- elysia@1.4.28(@sinclair/typebox@0.34.49)(exact-mirror@1.0.0)(file-type@22.0.1)(openapi-types@12.1.3)(typescript@6.0.3):
+ elysia@1.4.28(@sinclair/typebox@0.34.49)(exact-mirror@1.0.0)(file-type@22.0.1(supports-color@7.2.0))(openapi-types@12.1.3)(typescript@7.0.2):
dependencies:
'@sinclair/typebox': 0.34.49
cookie: 1.1.1
exact-mirror: 1.0.0
fast-decode-uri-component: 1.0.1
- file-type: 22.0.1
+ file-type: 22.0.1(supports-color@7.2.0)
memoirist: 0.4.0
openapi-types: 12.1.3
optionalDependencies:
- typescript: 6.0.3
+ typescript: 7.0.2
emoji-regex@8.0.0: {}
@@ -10590,20 +10776,20 @@ snapshots:
expect-type@1.3.0: {}
- express@5.2.1:
+ express@5.2.1(supports-color@7.2.0):
dependencies:
accepts: 2.0.0
- body-parser: 2.2.2
+ body-parser: 2.2.2(supports-color@7.2.0)
content-disposition: 1.1.0
content-type: 1.0.5
cookie: 0.7.2
cookie-signature: 1.2.2
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
depd: 2.0.0
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
- finalhandler: 2.1.1
+ finalhandler: 2.1.1(supports-color@7.2.0)
fresh: 2.0.0
http-errors: 2.0.1
merge-descriptors: 2.0.0
@@ -10614,9 +10800,9 @@ snapshots:
proxy-addr: 2.0.7
qs: 6.15.2
range-parser: 1.2.1
- router: 2.2.0
- send: 1.2.1
- serve-static: 2.2.1
+ router: 2.2.0(supports-color@7.2.0)
+ send: 1.2.1(supports-color@7.2.0)
+ serve-static: 2.2.1(supports-color@7.2.0)
statuses: 2.0.2
type-is: 2.1.0
vary: 1.1.2
@@ -10708,9 +10894,9 @@ snapshots:
dependencies:
is-unicode-supported: 2.1.0
- file-type@22.0.1:
+ file-type@22.0.1(supports-color@7.2.0):
dependencies:
- '@tokenizer/inflate': 0.4.1
+ '@tokenizer/inflate': 0.4.1(supports-color@7.2.0)
strtok3: 10.3.5
token-types: 6.1.2
uint8array-extras: 1.5.0
@@ -10721,9 +10907,9 @@ snapshots:
dependencies:
to-regex-range: 5.0.1
- finalhandler@2.1.1:
+ finalhandler@2.1.1(supports-color@7.2.0):
dependencies:
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
encodeurl: 2.0.0
escape-html: 1.0.3
on-finished: 2.4.1
@@ -10779,18 +10965,18 @@ snapshots:
fsevents@2.3.3:
optional: true
- fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3):
+ fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3):
dependencies:
'@orama/orama': 3.1.18
estree-util-value-to-estree: 3.5.0
github-slugger: 2.0.0
- hast-util-to-estree: 3.1.3
- hast-util-to-jsx-runtime: 2.3.6
+ hast-util-to-estree: 3.1.3(supports-color@7.2.0)
+ hast-util-to-jsx-runtime: 2.3.6(supports-color@7.2.0)
js-yaml: 4.1.1
- mdast-util-mdx: 3.0.0
+ mdast-util-mdx: 3.0.0(supports-color@7.2.0)
mdast-util-to-markdown: 2.1.2
- remark: 15.0.1
- remark-gfm: 4.0.1
+ remark: 15.0.1(supports-color@7.2.0)
+ remark-gfm: 4.0.1(supports-color@7.2.0)
remark-rehype: 11.1.2
scroll-into-view-if-needed: 3.1.0
shiki: 4.1.0
@@ -10799,30 +10985,30 @@ snapshots:
unist-util-visit: 5.1.0
vfile: 6.0.3
optionalDependencies:
- '@mdx-js/mdx': 3.1.1
+ '@mdx-js/mdx': 3.1.1(supports-color@7.2.0)
'@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@types/estree-jsx': 1.0.5
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
'@types/react': 19.2.15
lucide-react: 1.17.0(react@19.2.6)
- next: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ next: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
zod: 4.4.3
transitivePeerDependencies:
- supports-color
- fumadocs-mdx@15.0.10(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ fumadocs-mdx@15.0.10(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
- '@mdx-js/mdx': 3.1.1
+ '@mdx-js/mdx': 3.1.1(supports-color@7.2.0)
'@standard-schema/spec': 1.1.0
chokidar: 5.0.0
esbuild: 0.28.0
estree-util-value-to-estree: 3.5.0
- fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
+ fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
js-yaml: 4.1.1
- mdast-util-mdx: 3.0.0
+ mdast-util-mdx: 3.0.0(supports-color@7.2.0)
picocolors: 1.1.1
picomatch: 4.0.4
tinyexec: 1.2.3
@@ -10836,27 +11022,27 @@ snapshots:
'@types/mdast': 4.0.4
'@types/mdx': 2.0.13
'@types/react': 19.2.15
- next: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ next: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
rolldown: 1.0.3
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- supports-color
- fumadocs-twoslash@3.2.0(1d030e86b7f7b9132e23a7c3ab6b57d0):
+ fumadocs-twoslash@3.2.0(b4a625e5e99ec7f2a6e049efc2c17627):
dependencies:
'@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@shikijs/twoslash': 4.1.0(typescript@6.0.3)
- fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
- fumadocs-ui: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)
- mdast-util-from-markdown: 2.0.3
- mdast-util-gfm: 3.1.0
+ '@shikijs/twoslash': 4.1.0(supports-color@7.2.0)(typescript@7.0.2)
+ fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
+ fumadocs-ui: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)
+ mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
+ mdast-util-gfm: 3.1.0(supports-color@7.2.0)
mdast-util-to-hast: 13.2.1
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
shiki: 4.1.0
tailwind-merge: 3.6.0
- twoslash: 0.3.8(typescript@6.0.3)
+ twoslash: 0.3.8(supports-color@7.2.0)(typescript@7.0.2)
optionalDependencies:
'@types/react': 19.2.15
transitivePeerDependencies:
@@ -10864,7 +11050,7 @@ snapshots:
- supports-color
- typescript
- fumadocs-ui@16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0):
+ fumadocs-ui@16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0):
dependencies:
'@fumadocs/tailwind': 0.0.5(@tailwindcss/oxide@4.3.0)(tailwindcss@4.3.0)
'@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -10878,7 +11064,7 @@ snapshots:
'@radix-ui/react-slot': 1.2.4(@types/react@19.2.15)(react@19.2.6)
'@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
class-variance-authority: 0.7.1
- fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
+ fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
lucide-react: 1.17.0(react@19.2.6)
motion: 12.40.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
next-themes: 0.4.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -10893,7 +11079,7 @@ snapshots:
optionalDependencies:
'@types/mdx': 2.0.13
'@types/react': 19.2.15
- next: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ next: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
transitivePeerDependencies:
- '@emotion/is-prop-valid'
- '@tailwindcss/oxide'
@@ -11035,7 +11221,7 @@ snapshots:
web-namespaces: 2.0.1
zwitch: 2.0.4
- hast-util-to-estree@3.1.3:
+ hast-util-to-estree@3.1.3(supports-color@7.2.0):
dependencies:
'@types/estree': 1.0.9
'@types/estree-jsx': 1.0.5
@@ -11045,9 +11231,9 @@ snapshots:
estree-util-attach-comments: 3.0.0
estree-util-is-identifier-name: 3.0.0
hast-util-whitespace: 3.0.0
- mdast-util-mdx-expression: 2.0.1
- mdast-util-mdx-jsx: 3.2.0
- mdast-util-mdxjs-esm: 2.0.1
+ mdast-util-mdx-expression: 2.0.1(supports-color@7.2.0)
+ mdast-util-mdx-jsx: 3.2.0(supports-color@7.2.0)
+ mdast-util-mdxjs-esm: 2.0.1(supports-color@7.2.0)
property-information: 7.1.0
space-separated-tokens: 2.0.2
style-to-js: 1.1.21
@@ -11070,7 +11256,7 @@ snapshots:
stringify-entities: 4.0.4
zwitch: 2.0.4
- hast-util-to-jsx-runtime@2.3.6:
+ hast-util-to-jsx-runtime@2.3.6(supports-color@7.2.0):
dependencies:
'@types/estree': 1.0.9
'@types/hast': 3.0.4
@@ -11079,9 +11265,9 @@ snapshots:
devlop: 1.1.0
estree-util-is-identifier-name: 3.0.0
hast-util-whitespace: 3.0.0
- mdast-util-mdx-expression: 2.0.1
- mdast-util-mdx-jsx: 3.2.0
- mdast-util-mdxjs-esm: 2.0.1
+ mdast-util-mdx-expression: 2.0.1(supports-color@7.2.0)
+ mdast-util-mdx-jsx: 3.2.0(supports-color@7.2.0)
+ mdast-util-mdxjs-esm: 2.0.1(supports-color@7.2.0)
property-information: 7.1.0
space-separated-tokens: 2.0.2
style-to-js: 1.1.21
@@ -11136,17 +11322,17 @@ snapshots:
statuses: 2.0.2
toidentifier: 1.0.1
- http-proxy-agent@7.0.2:
+ http-proxy-agent@7.0.2(supports-color@7.2.0):
dependencies:
agent-base: 7.1.4
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
- https-proxy-agent@7.0.6:
+ https-proxy-agent@7.0.6(supports-color@7.2.0):
dependencies:
agent-base: 7.1.4
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
@@ -11272,7 +11458,7 @@ snapshots:
dependencies:
argparse: 2.0.1
- jsdom@28.1.0(@noble/hashes@1.8.0):
+ jsdom@28.1.0(@noble/hashes@1.8.0)(supports-color@7.2.0):
dependencies:
'@acemir/cssom': 0.9.31
'@asamuzakjp/dom-selector': 6.8.1
@@ -11282,8 +11468,8 @@ snapshots:
data-urls: 7.0.0(@noble/hashes@1.8.0)
decimal.js: 10.6.0
html-encoding-sniffer: 6.0.0(@noble/hashes@1.8.0)
- http-proxy-agent: 7.0.2
- https-proxy-agent: 7.0.6
+ http-proxy-agent: 7.0.2(supports-color@7.2.0)
+ https-proxy-agent: 7.0.6(supports-color@7.2.0)
is-potential-custom-element-name: 1.0.1
parse5: 8.0.1
saxes: 6.0.0
@@ -11422,12 +11608,6 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
- magicast@0.5.3:
- dependencies:
- '@babel/parser': 7.29.7
- '@babel/types': 7.29.7
- source-map-js: 1.2.1
-
magicast@0.5.4:
dependencies:
'@babel/parser': 7.29.7
@@ -11453,14 +11633,14 @@ snapshots:
unist-util-is: 6.0.1
unist-util-visit-parents: 6.0.2
- mdast-util-from-markdown@2.0.3:
+ mdast-util-from-markdown@2.0.3(supports-color@7.2.0):
dependencies:
'@types/mdast': 4.0.4
'@types/unist': 3.0.3
decode-named-character-reference: 1.3.0
devlop: 1.1.0
mdast-util-to-string: 4.0.0
- micromark: 4.0.2
+ micromark: 4.0.2(supports-color@7.2.0)
micromark-util-decode-numeric-character-reference: 2.0.2
micromark-util-decode-string: 2.0.1
micromark-util-normalize-identifier: 2.0.1
@@ -11478,67 +11658,67 @@ snapshots:
mdast-util-find-and-replace: 3.0.2
micromark-util-character: 2.1.1
- mdast-util-gfm-footnote@2.1.0:
+ mdast-util-gfm-footnote@2.1.0(supports-color@7.2.0):
dependencies:
'@types/mdast': 4.0.4
devlop: 1.1.0
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
mdast-util-to-markdown: 2.1.2
micromark-util-normalize-identifier: 2.0.1
transitivePeerDependencies:
- supports-color
- mdast-util-gfm-strikethrough@2.0.0:
+ mdast-util-gfm-strikethrough@2.0.0(supports-color@7.2.0):
dependencies:
'@types/mdast': 4.0.4
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
- mdast-util-gfm-table@2.0.0:
+ mdast-util-gfm-table@2.0.0(supports-color@7.2.0):
dependencies:
'@types/mdast': 4.0.4
devlop: 1.1.0
markdown-table: 3.0.4
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
- mdast-util-gfm-task-list-item@2.0.0:
+ mdast-util-gfm-task-list-item@2.0.0(supports-color@7.2.0):
dependencies:
'@types/mdast': 4.0.4
devlop: 1.1.0
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
- mdast-util-gfm@3.1.0:
+ mdast-util-gfm@3.1.0(supports-color@7.2.0):
dependencies:
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
mdast-util-gfm-autolink-literal: 2.0.1
- mdast-util-gfm-footnote: 2.1.0
- mdast-util-gfm-strikethrough: 2.0.0
- mdast-util-gfm-table: 2.0.0
- mdast-util-gfm-task-list-item: 2.0.0
+ mdast-util-gfm-footnote: 2.1.0(supports-color@7.2.0)
+ mdast-util-gfm-strikethrough: 2.0.0(supports-color@7.2.0)
+ mdast-util-gfm-table: 2.0.0(supports-color@7.2.0)
+ mdast-util-gfm-task-list-item: 2.0.0(supports-color@7.2.0)
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
- mdast-util-mdx-expression@2.0.1:
+ mdast-util-mdx-expression@2.0.1(supports-color@7.2.0):
dependencies:
'@types/estree-jsx': 1.0.5
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
devlop: 1.1.0
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
- mdast-util-mdx-jsx@3.2.0:
+ mdast-util-mdx-jsx@3.2.0(supports-color@7.2.0):
dependencies:
'@types/estree-jsx': 1.0.5
'@types/hast': 3.0.4
@@ -11546,7 +11726,7 @@ snapshots:
'@types/unist': 3.0.3
ccount: 2.0.1
devlop: 1.1.0
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
mdast-util-to-markdown: 2.1.2
parse-entities: 4.0.2
stringify-entities: 4.0.4
@@ -11555,23 +11735,23 @@ snapshots:
transitivePeerDependencies:
- supports-color
- mdast-util-mdx@3.0.0:
+ mdast-util-mdx@3.0.0(supports-color@7.2.0):
dependencies:
- mdast-util-from-markdown: 2.0.3
- mdast-util-mdx-expression: 2.0.1
- mdast-util-mdx-jsx: 3.2.0
- mdast-util-mdxjs-esm: 2.0.1
+ mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
+ mdast-util-mdx-expression: 2.0.1(supports-color@7.2.0)
+ mdast-util-mdx-jsx: 3.2.0(supports-color@7.2.0)
+ mdast-util-mdxjs-esm: 2.0.1(supports-color@7.2.0)
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
- mdast-util-mdxjs-esm@2.0.1:
+ mdast-util-mdxjs-esm@2.0.1(supports-color@7.2.0):
dependencies:
'@types/estree-jsx': 1.0.5
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
devlop: 1.1.0
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
@@ -11893,10 +12073,10 @@ snapshots:
micromark-util-types@2.0.2: {}
- micromark@4.0.2:
+ micromark@4.0.2(supports-color@7.2.0):
dependencies:
'@types/debug': 4.1.13
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
decode-named-character-reference: 1.3.0
devlop: 1.1.0
micromark-core-commonmark: 2.0.3
@@ -11982,7 +12162,7 @@ snapshots:
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- next@16.2.6(@babel/core@7.29.7)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
+ next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
dependencies:
'@next/env': 16.2.6
'@swc/helpers': 0.5.15
@@ -11991,7 +12171,7 @@ snapshots:
postcss: 8.4.31
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.6)
+ styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.2.6)
optionalDependencies:
'@next/swc-darwin-arm64': 16.2.6
'@next/swc-darwin-x64': 16.2.6
@@ -12006,58 +12186,9 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
- next@16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
- dependencies:
- '@next/env': 16.2.6
- '@swc/helpers': 0.5.15
- baseline-browser-mapping: 2.10.33
- caniuse-lite: 1.0.30001793
- postcss: 8.4.31
- react: 19.2.4
- react-dom: 19.2.4(react@19.2.4)
- styled-jsx: 5.1.6(react@19.2.4)
- optionalDependencies:
- '@next/swc-darwin-arm64': 16.2.6
- '@next/swc-darwin-x64': 16.2.6
- '@next/swc-linux-arm64-gnu': 16.2.6
- '@next/swc-linux-arm64-musl': 16.2.6
- '@next/swc-linux-x64-gnu': 16.2.6
- '@next/swc-linux-x64-musl': 16.2.6
- '@next/swc-win32-arm64-msvc': 16.2.6
- '@next/swc-win32-x64-msvc': 16.2.6
- sharp: 0.34.5
- transitivePeerDependencies:
- - '@babel/core'
- - babel-plugin-macros
-
- next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
- dependencies:
- '@next/env': 16.2.6
- '@swc/helpers': 0.5.15
- baseline-browser-mapping: 2.10.33
- caniuse-lite: 1.0.30001793
- postcss: 8.4.31
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.6)
- optionalDependencies:
- '@next/swc-darwin-arm64': 16.2.6
- '@next/swc-darwin-x64': 16.2.6
- '@next/swc-linux-arm64-gnu': 16.2.6
- '@next/swc-linux-arm64-musl': 16.2.6
- '@next/swc-linux-x64-gnu': 16.2.6
- '@next/swc-linux-x64-musl': 16.2.6
- '@next/swc-win32-arm64-msvc': 16.2.6
- '@next/swc-win32-x64-msvc': 16.2.6
- sharp: 0.34.5
- transitivePeerDependencies:
- - '@babel/core'
- - babel-plugin-macros
- optional: true
-
nf3@0.3.17: {}
- nitro@3.0.260522-beta(chokidar@5.0.0)(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3))(jiti@2.7.0)(lru-cache@11.5.1)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ nitro@3.0.260522-beta(chokidar@5.0.0)(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3))(jiti@2.7.0)(lru-cache@11.5.1)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
consola: 3.4.2
crossws: 0.4.5(srvx@0.11.16)
@@ -12075,7 +12206,7 @@ snapshots:
unstorage: 2.0.0-alpha.7(chokidar@5.0.0)(db0@0.3.4(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3)))(lru-cache@11.5.1)(ofetch@2.0.0-alpha.3)
optionalDependencies:
jiti: 2.7.0
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -12447,11 +12578,6 @@ snapshots:
iconv-lite: 0.7.2
unpipe: 1.0.0
- react-dom@19.2.4(react@19.2.4):
- dependencies:
- react: 19.2.4
- scheduler: 0.27.0
-
react-dom@19.2.6(react@19.2.6):
dependencies:
react: 19.2.6
@@ -12486,8 +12612,6 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.15
- react@19.2.4: {}
-
react@19.2.6: {}
read-package-json-fast@6.0.0:
@@ -12553,36 +12677,36 @@ snapshots:
hast-util-raw: 9.1.0
vfile: 6.0.3
- rehype-recma@1.0.0:
+ rehype-recma@1.0.0(supports-color@7.2.0):
dependencies:
'@types/estree': 1.0.9
'@types/hast': 3.0.4
- hast-util-to-estree: 3.1.3
+ hast-util-to-estree: 3.1.3(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
- remark-gfm@4.0.1:
+ remark-gfm@4.0.1(supports-color@7.2.0):
dependencies:
'@types/mdast': 4.0.4
- mdast-util-gfm: 3.1.0
+ mdast-util-gfm: 3.1.0(supports-color@7.2.0)
micromark-extension-gfm: 3.0.0
- remark-parse: 11.0.0
+ remark-parse: 11.0.0(supports-color@7.2.0)
remark-stringify: 11.0.0
unified: 11.0.5
transitivePeerDependencies:
- supports-color
- remark-mdx@3.1.1:
+ remark-mdx@3.1.1(supports-color@7.2.0):
dependencies:
- mdast-util-mdx: 3.0.0
+ mdast-util-mdx: 3.0.0(supports-color@7.2.0)
micromark-extension-mdxjs: 3.0.0
transitivePeerDependencies:
- supports-color
- remark-parse@11.0.0:
+ remark-parse@11.0.0(supports-color@7.2.0):
dependencies:
'@types/mdast': 4.0.4
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
micromark-util-types: 2.0.2
unified: 11.0.5
transitivePeerDependencies:
@@ -12602,10 +12726,10 @@ snapshots:
mdast-util-to-markdown: 2.1.2
unified: 11.0.5
- remark@15.0.1:
+ remark@15.0.1(supports-color@7.2.0):
dependencies:
'@types/mdast': 4.0.4
- remark-parse: 11.0.0
+ remark-parse: 11.0.0(supports-color@7.2.0)
remark-stringify: 11.0.0
unified: 11.0.5
transitivePeerDependencies:
@@ -12632,7 +12756,7 @@ snapshots:
robust-predicates@3.0.3: {}
- rolldown-plugin-dts@0.25.2(rolldown@1.0.3)(typescript@6.0.3):
+ rolldown-plugin-dts@0.25.2(rolldown@1.0.3)(typescript@7.0.2):
dependencies:
'@babel/generator': 8.0.0-rc.6
'@babel/helper-validator-identifier': 8.0.0-rc.6
@@ -12644,7 +12768,7 @@ snapshots:
obug: 2.1.1
rolldown: 1.0.3
optionalDependencies:
- typescript: 6.0.3
+ typescript: 7.0.2
transitivePeerDependencies:
- oxc-resolver
@@ -12678,9 +12802,9 @@ snapshots:
points-on-curve: 0.2.0
points-on-path: 0.2.1
- router@2.2.0:
+ router@2.2.0(supports-color@7.2.0):
dependencies:
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
depd: 2.0.0
is-promise: 4.0.0
parseurl: 1.3.3
@@ -12724,9 +12848,9 @@ snapshots:
semver@7.8.1: {}
- send@1.2.1:
+ send@1.2.1(supports-color@7.2.0):
dependencies:
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
@@ -12752,12 +12876,12 @@ snapshots:
seroval@1.6.4: {}
- serve-static@2.2.1:
+ serve-static@2.2.1(supports-color@7.2.0):
dependencies:
encodeurl: 2.0.0
escape-html: 1.0.3
parseurl: 1.3.3
- send: 1.2.1
+ send: 1.2.1(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
@@ -12869,10 +12993,10 @@ snapshots:
seroval: 1.5.4
seroval-plugins: 1.5.4(seroval@1.5.4)
- solid-refresh@0.6.3(solid-js@1.9.13):
+ solid-refresh@0.6.3(solid-js@1.9.13)(supports-color@7.2.0):
dependencies:
'@babel/generator': 7.29.7
- '@babel/helper-module-imports': 7.29.7
+ '@babel/helper-module-imports': 7.29.7(supports-color@7.2.0)
'@babel/types': 7.29.7
solid-js: 1.9.13
transitivePeerDependencies:
@@ -12946,25 +13070,20 @@ snapshots:
dependencies:
inline-style-parser: 0.2.7
- styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.6):
+ styled-jsx@5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.2.6):
dependencies:
client-only: 0.0.1
react: 19.2.6
optionalDependencies:
- '@babel/core': 7.29.7
-
- styled-jsx@5.1.6(react@19.2.4):
- dependencies:
- client-only: 0.0.1
- react: 19.2.4
+ '@babel/core': 7.29.7(supports-color@7.2.0)
stylis@4.4.0: {}
- superagent@10.3.0:
+ superagent@10.3.0(supports-color@7.2.0):
dependencies:
component-emitter: 1.3.1
cookiejar: 2.1.4
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
fast-safe-stringify: 2.1.1
form-data: 4.0.5
formidable: 3.5.4
@@ -12974,11 +13093,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- supertest@7.2.2:
+ supertest@7.2.2(supports-color@7.2.0):
dependencies:
cookie-signature: 1.2.2
methods: 1.1.2
- superagent: 10.3.0
+ superagent: 10.3.0(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
@@ -12986,7 +13105,7 @@ snapshots:
dependencies:
has-flag: 4.0.0
- svelte-check@4.5.0(picomatch@4.0.7)(svelte@5.56.0)(typescript@6.0.3):
+ svelte-check@4.5.0(picomatch@4.0.7)(svelte@5.56.0)(typescript@7.0.2):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
chokidar: 4.0.3
@@ -12994,16 +13113,16 @@ snapshots:
picocolors: 1.1.1
sade: 1.8.1
svelte: 5.56.0
- typescript: 6.0.3
+ typescript: 7.0.2
transitivePeerDependencies:
- picomatch
- svelte2tsx@0.7.55(svelte@5.56.0)(typescript@6.0.3):
+ svelte2tsx@0.7.55(svelte@5.56.0)(typescript@7.0.2):
dependencies:
dedent-js: 1.0.1
scule: 1.3.0
svelte: 5.56.0
- typescript: 6.0.3
+ typescript: 7.0.2
svelte@5.56.0:
dependencies:
@@ -13108,11 +13227,11 @@ snapshots:
ts-dedent@2.2.0: {}
- tsconfck@3.1.6(typescript@6.0.3):
+ tsconfck@3.1.6(typescript@7.0.2):
optionalDependencies:
- typescript: 6.0.3
+ typescript: 7.0.2
- tsdown@0.22.1(tsx@4.22.4)(typescript@6.0.3):
+ tsdown@0.22.1(tsx@4.22.4)(typescript@7.0.2):
dependencies:
ansis: 4.3.0
cac: 7.0.0
@@ -13123,7 +13242,7 @@ snapshots:
obug: 2.1.1
picomatch: 4.0.4
rolldown: 1.0.3
- rolldown-plugin-dts: 0.25.2(rolldown@1.0.3)(typescript@6.0.3)
+ rolldown-plugin-dts: 0.25.2(rolldown@1.0.3)(typescript@7.0.2)
semver: 7.8.1
tinyexec: 1.2.3
tinyglobby: 0.2.17
@@ -13131,7 +13250,7 @@ snapshots:
unconfig-core: 7.5.0
optionalDependencies:
tsx: 4.22.4
- typescript: 6.0.3
+ typescript: 7.0.2
transitivePeerDependencies:
- '@ts-macro/tsc'
- '@typescript/native-preview'
@@ -13146,22 +13265,22 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
- turbo@2.9.16:
+ turbo@2.10.12:
optionalDependencies:
- '@turbo/darwin-64': 2.9.16
- '@turbo/darwin-arm64': 2.9.16
- '@turbo/linux-64': 2.9.16
- '@turbo/linux-arm64': 2.9.16
- '@turbo/windows-64': 2.9.16
- '@turbo/windows-arm64': 2.9.16
+ '@turbo/darwin-64': 2.10.12
+ '@turbo/darwin-arm64': 2.10.12
+ '@turbo/linux-64': 2.10.12
+ '@turbo/linux-arm64': 2.10.12
+ '@turbo/windows-64': 2.10.12
+ '@turbo/windows-arm64': 2.10.12
twoslash-protocol@0.3.8: {}
- twoslash@0.3.8(typescript@6.0.3):
+ twoslash@0.3.8(supports-color@7.2.0)(typescript@7.0.2):
dependencies:
- '@typescript/vfs': 1.6.4(typescript@6.0.3)
+ '@typescript/vfs': 1.6.4(supports-color@7.2.0)(typescript@7.0.2)
twoslash-protocol: 0.3.8
- typescript: 6.0.3
+ typescript: 7.0.2
transitivePeerDependencies:
- supports-color
@@ -13175,8 +13294,33 @@ snapshots:
media-typer: 1.1.0
mime-types: 3.0.2
+ typescript@5.9.3: {}
+
typescript@6.0.3: {}
+ typescript@7.0.2:
+ optionalDependencies:
+ '@typescript/typescript-aix-ppc64': 7.0.2
+ '@typescript/typescript-darwin-arm64': 7.0.2
+ '@typescript/typescript-darwin-x64': 7.0.2
+ '@typescript/typescript-freebsd-arm64': 7.0.2
+ '@typescript/typescript-freebsd-x64': 7.0.2
+ '@typescript/typescript-linux-arm': 7.0.2
+ '@typescript/typescript-linux-arm64': 7.0.2
+ '@typescript/typescript-linux-loong64': 7.0.2
+ '@typescript/typescript-linux-mips64el': 7.0.2
+ '@typescript/typescript-linux-ppc64': 7.0.2
+ '@typescript/typescript-linux-riscv64': 7.0.2
+ '@typescript/typescript-linux-s390x': 7.0.2
+ '@typescript/typescript-linux-x64': 7.0.2
+ '@typescript/typescript-netbsd-arm64': 7.0.2
+ '@typescript/typescript-netbsd-x64': 7.0.2
+ '@typescript/typescript-openbsd-arm64': 7.0.2
+ '@typescript/typescript-openbsd-x64': 7.0.2
+ '@typescript/typescript-sunos-x64': 7.0.2
+ '@typescript/typescript-win32-arm64': 7.0.2
+ '@typescript/typescript-win32-x64': 7.0.2
+
ufo@1.6.4: {}
uint8array-extras@1.5.0: {}
@@ -13216,10 +13360,6 @@ snapshots:
quansync: 1.0.0
unconfig-core: 7.5.0
- undici-types@6.21.0: {}
-
- undici-types@7.16.0: {}
-
undici-types@7.24.6: {}
undici@7.27.2: {}
@@ -13277,7 +13417,7 @@ snapshots:
unplugin@3.0.0:
dependencies:
'@jridgewell/remapping': 2.3.5
- picomatch: 4.0.4
+ picomatch: 4.0.7
webpack-virtual-modules: 0.6.2
unstorage@2.0.0-alpha.7(chokidar@5.0.0)(db0@0.3.4(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3)))(lru-cache@11.5.1)(ofetch@2.0.0-alpha.3):
@@ -13333,46 +13473,14 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
- vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
- dependencies:
- '@babel/core': 7.29.7
- '@types/babel__core': 7.20.5
- babel-preset-solid: 1.9.12(@babel/core@7.29.7)(solid-js@1.9.13)
- merge-anything: 5.1.7
- solid-js: 1.9.13
- solid-refresh: 0.6.3(solid-js@1.9.13)
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitefu: 1.1.3(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- optionalDependencies:
- '@testing-library/jest-dom': 6.9.1
- transitivePeerDependencies:
- - supports-color
- optional: true
-
- vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
- dependencies:
- '@babel/core': 7.29.7
- '@types/babel__core': 7.20.5
- babel-preset-solid: 1.9.12(@babel/core@7.29.7)(solid-js@1.9.13)
- merge-anything: 5.1.7
- solid-js: 1.9.13
- solid-refresh: 0.6.3(solid-js@1.9.13)
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitefu: 1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- optionalDependencies:
- '@testing-library/jest-dom': 6.9.1
- transitivePeerDependencies:
- - supports-color
- optional: true
-
- vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.7(supports-color@7.2.0)
'@types/babel__core': 7.20.5
- babel-preset-solid: 1.9.12(@babel/core@7.29.7)(solid-js@1.9.13)
+ babel-preset-solid: 1.9.12(@babel/core@7.29.7(supports-color@7.2.0))(solid-js@1.9.13)
merge-anything: 5.1.7
solid-js: 1.9.13
- solid-refresh: 0.6.3(solid-js@1.9.13)
+ solid-refresh: 0.6.3(solid-js@1.9.13)(supports-color@7.2.0)
vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
vitefu: 1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
optionalDependencies:
@@ -13380,50 +13488,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
- vite-tsconfig-paths@6.1.1(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vite-tsconfig-paths@6.1.1(supports-color@7.2.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
- debug: 4.4.3
+ debug: 4.4.3(supports-color@7.2.0)
globrex: 0.1.2
- tsconfck: 3.1.6(typescript@6.0.3)
+ tsconfck: 3.1.6(typescript@7.0.2)
vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- supports-color
- typescript
- vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0):
- dependencies:
- lightningcss: 1.32.0
- picomatch: 4.0.4
- postcss: 8.5.15
- rolldown: 1.0.3
- tinyglobby: 0.2.17
- optionalDependencies:
- '@types/node': 22.19.20
- esbuild: 0.28.0
- fsevents: 2.3.3
- jiti: 2.7.0
- tsx: 4.22.4
- yaml: 2.9.0
-
- vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0):
- dependencies:
- lightningcss: 1.32.0
- picomatch: 4.0.4
- postcss: 8.5.15
- rolldown: 1.0.3
- tinyglobby: 0.2.17
- optionalDependencies:
- '@types/node': 24.10.0
- esbuild: 0.28.0
- fsevents: 2.3.3
- jiti: 2.7.0
- tsx: 4.22.4
- yaml: 2.9.0
-
vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0):
dependencies:
lightningcss: 1.32.0
- picomatch: 4.0.4
+ picomatch: 4.0.7
postcss: 8.5.15
rolldown: 1.0.3
tinyglobby: 0.2.17
@@ -13435,49 +13513,11 @@ snapshots:
tsx: 4.22.4
yaml: 2.9.0
- vitefu@1.1.3(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
- optionalDependencies:
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
-
- vitefu@1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
- optionalDependencies:
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
-
vitefu@1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
optionalDependencies:
vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitest@4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
- dependencies:
- '@vitest/expect': 4.1.8
- '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- '@vitest/pretty-format': 4.1.8
- '@vitest/runner': 4.1.8
- '@vitest/snapshot': 4.1.8
- '@vitest/spy': 4.1.8
- '@vitest/utils': 4.1.8
- es-module-lexer: 2.1.0
- expect-type: 1.3.0
- magic-string: 0.30.21
- obug: 2.1.1
- pathe: 2.0.3
- picomatch: 4.0.4
- std-env: 4.1.0
- tinybench: 2.9.0
- tinyexec: 1.2.3
- tinyglobby: 0.2.17
- tinyrainbow: 3.1.0
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- why-is-node-running: 2.3.0
- optionalDependencies:
- '@types/node': 22.19.20
- '@vitest/coverage-v8': 4.1.8(vitest@4.1.8)
- happy-dom: 20.9.0
- jsdom: 28.1.0(@noble/hashes@1.8.0)
- transitivePeerDependencies:
- - msw
-
- vitest@4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vitest@4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0)(supports-color@7.2.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
'@vitest/expect': 4.1.8
'@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -13491,10 +13531,10 @@ snapshots:
magic-string: 0.30.21
obug: 2.1.1
pathe: 2.0.3
- picomatch: 4.0.4
+ picomatch: 4.0.7
std-env: 4.1.0
tinybench: 2.9.0
- tinyexec: 1.2.3
+ tinyexec: 1.3.0
tinyglobby: 0.2.17
tinyrainbow: 3.1.0
vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
@@ -13503,7 +13543,7 @@ snapshots:
'@types/node': 25.9.1
'@vitest/coverage-v8': 4.1.8(vitest@4.1.8)
happy-dom: 20.9.0
- jsdom: 28.1.0(@noble/hashes@1.8.0)
+ jsdom: 28.1.0(@noble/hashes@1.8.0)(supports-color@7.2.0)
transitivePeerDependencies:
- msw
@@ -13511,21 +13551,21 @@ snapshots:
vue-component-type-helpers@3.3.3: {}
- vue-tsc@3.3.3(typescript@6.0.3):
+ vue-tsc@3.3.3(typescript@7.0.2):
dependencies:
- '@volar/typescript': 2.4.28
+ '@volar/typescript': 2.4.28(typescript@7.0.2)
'@vue/language-core': 3.3.3
- typescript: 6.0.3
+ typescript: 7.0.2
- vue@3.5.35(typescript@6.0.3):
+ vue@3.5.35(typescript@7.0.2):
dependencies:
'@vue/compiler-dom': 3.5.35
'@vue/compiler-sfc': 3.5.35
'@vue/runtime-dom': 3.5.35
- '@vue/server-renderer': 3.5.35(vue@3.5.35(typescript@6.0.3))
+ '@vue/server-renderer': 3.5.35(vue@3.5.35(typescript@7.0.2))
'@vue/shared': 3.5.35
optionalDependencies:
- typescript: 6.0.3
+ typescript: 7.0.2
w3c-xmlserializer@5.0.0:
dependencies:
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 01269547..49e0d1be 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,4 +1,3 @@
-shellEmulator: true
packages:
- permix
- docs
@@ -7,3 +6,33 @@ allowBuilds:
better-sqlite3: true
esbuild: false
sharp: false
+catalog:
+ '@types/node': ^25.9.1
+ '@types/react': ^19.2.15
+ '@types/react-dom': ^19.2.3
+ '@vitejs/plugin-react': ^6.0.2
+ oxfmt: ^0.64.0
+ oxlint: ^1.79.0
+ oxlint-tsgolint: ^7.0.2001
+ react: ^19.2.6
+ react-dom: ^19.2.6
+ tsx: ^4.22.4
+ turbo: 2.10.12
+ typescript: 7.0.2
+ ultracite: ^7.10.6
+ vite: ^8.0.16
+ vitest: ^4.1.8
+catalogs:
+ typescript-classic:
+ typescript59: npm:typescript@5.9.3
+ typescript6: npm:@typescript/typescript6@6.0.2
+catalogMode: strict
+saveWorkspaceProtocol: rolling
+strictPeerDependencies: true
+peerDependencyRules:
+ allowedVersions:
+ typescript: '*'
+ drizzle-orm>effect: '*'
+publicHoistPattern:
+ - '@typescript/*'
+shellEmulator: true
diff --git a/turbo.json b/turbo.json
index ef21be9f..341f3082 100644
--- a/turbo.json
+++ b/turbo.json
@@ -1,8 +1,37 @@
{
- "$schema": "./node_modules/turbo/schema.json",
+ "$schema": "https://v2-10-12.turborepo.dev/schema.json",
+ "ui": "tui",
+ "concurrency": "100%",
+ "cacheMaxAge": "14d",
+ "cacheMaxSize": "10GB",
"tasks": {
+ "transit": {
+ "dependsOn": ["^transit"]
+ },
+ "build": {
+ "dependsOn": ["^build"],
+ "inputs": ["$TURBO_DEFAULT$"],
+ "outputs": [
+ "dist/**",
+ ".next/**",
+ "!.next/cache/**",
+ "!.next/dev/**",
+ ".source/**",
+ ".output/**"
+ ]
+ },
"check-types": {
- "cache": false
+ "dependsOn": ["transit", "permix#build"],
+ "inputs": [
+ "$TURBO_DEFAULT$",
+ {
+ "mode": "jit",
+ "globs": [".source/**"]
+ }
+ ]
+ },
+ "test": {
+ "dependsOn": ["transit", "permix#build"]
}
}
}
From 6de7bb87d54772ce1af2fee3341fa2fbee6845bd Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 13:04:13 +0200
Subject: [PATCH 02/28] feat: add createPermix factory with isolated React
context
Give each factory its own provider/hook/Check bindings so nested policies do not share state, and hydrate dehydrated rules on the first client render without mutating during render.
Co-authored-by: Cursor
---
.github/scripts/select-react-catalog.mjs | 17 ++
.github/workflows/react-compatibility.yml | 61 ++++++
docs/content/docs/integrations/react.mdx | 150 +++++++------
.../docs/integrations/tanstack-start.mdx | 2 +-
examples/react/src/hooks/permissions.ts | 6 +-
examples/react/src/lib/permix.ts | 7 +-
examples/react/src/main.tsx | 5 +-
oxlint.config.ts | 8 +
permix/skills/permix/SKILL.md | 2 +-
permix/skills/permix/references/frontend.md | 90 ++++----
permix/src/react/components.test.tsx | 149 ++++++++++++-
permix/src/react/components.tsx | 116 +++++++---
permix/src/react/create-permix.test.tsx | 205 ++++++++++++++++++
permix/src/react/create-permix.tsx | 75 +++++++
permix/src/react/hooks.test.tsx | 31 +++
permix/src/react/hooks.ts | 40 +++-
permix/src/react/index.ts | 3 +
.../react/use-effect-event-compat.test.tsx | 29 +++
permix/src/react/use-effect-event.ts | 29 +++
.../src/react/use-isomorphic-layout-effect.ts | 4 +
permix/test-d/public-api.ts | 50 +++--
permix/tsconfig.compat.json | 13 +-
22 files changed, 903 insertions(+), 189 deletions(-)
create mode 100644 .github/scripts/select-react-catalog.mjs
create mode 100644 .github/workflows/react-compatibility.yml
create mode 100644 permix/src/react/create-permix.test.tsx
create mode 100644 permix/src/react/create-permix.tsx
create mode 100644 permix/src/react/use-effect-event-compat.test.tsx
create mode 100644 permix/src/react/use-effect-event.ts
create mode 100644 permix/src/react/use-isomorphic-layout-effect.ts
diff --git a/.github/scripts/select-react-catalog.mjs b/.github/scripts/select-react-catalog.mjs
new file mode 100644
index 00000000..de5edb04
--- /dev/null
+++ b/.github/scripts/select-react-catalog.mjs
@@ -0,0 +1,17 @@
+import { readFileSync, writeFileSync } from 'node:fs'
+
+const react = process.env.REACT
+const reactTypes = process.env.REACT_TYPES
+const reactDomTypes = process.env.REACT_DOM_TYPES
+
+if (!react || !reactTypes || !reactDomTypes) {
+ throw new Error('REACT, REACT_TYPES, and REACT_DOM_TYPES must be set')
+}
+
+const yaml = readFileSync('pnpm-workspace.yaml', 'utf-8')
+ .replace(/^( {2}react: ).+$/m, `$1${react}`)
+ .replace(/^( {2}react-dom: ).+$/m, `$1${react}`)
+ .replace(/^( {2}'@types\/react': ).+$/m, `$1${reactTypes}`)
+ .replace(/^( {2}'@types\/react-dom': ).+$/m, `$1${reactDomTypes}`)
+
+writeFileSync('pnpm-workspace.yaml', yaml)
diff --git a/.github/workflows/react-compatibility.yml b/.github/workflows/react-compatibility.yml
new file mode 100644
index 00000000..7d7ebce3
--- /dev/null
+++ b/.github/workflows/react-compatibility.yml
@@ -0,0 +1,61 @@
+name: React Compatibility
+
+on:
+ pull_request:
+ types: [opened, synchronize]
+ branches:
+ - main
+
+jobs:
+ react-compatibility:
+ name: React ${{ matrix.react }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - react: '18.3.1'
+ react-types: '18.3.23'
+ react-dom-types: '18.3.7'
+ pin: true
+ - react: '19.2.6'
+ react-types: '19.2.15'
+ react-dom-types: '19.2.3'
+ pin: false
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 24
+ cache: pnpm
+
+ - name: Pin React catalog versions
+ if: matrix.pin
+ env:
+ REACT: ${{ matrix.react }}
+ REACT_TYPES: ${{ matrix.react-types }}
+ REACT_DOM_TYPES: ${{ matrix.react-dom-types }}
+ run: node .github/scripts/select-react-catalog.mjs
+
+ - name: Install pinned Permix dependencies
+ if: matrix.pin
+ run: pnpm install --filter permix --no-frozen-lockfile
+
+ - name: Install dependencies
+ if: ${{ !matrix.pin }}
+ run: pnpm install --frozen-lockfile
+
+ - name: Test React adapter
+ run: pnpm --filter permix exec vitest run src/react
+
+ - name: Build
+ run: pnpm --filter permix build
diff --git a/docs/content/docs/integrations/react.mdx b/docs/content/docs/integrations/react.mdx
index 2eb3e010..e7b91fb1 100644
--- a/docs/content/docs/integrations/react.mdx
+++ b/docs/content/docs/integrations/react.mdx
@@ -5,7 +5,9 @@ description: Learn how to use Permix with React applications
## Overview
-Permix provides official React integration through the `PermixProvider` component and `usePermix` hook. This allows you to manage permissions reactively in your React app.
+Permix provides official React integration through `createPermix` from `permix/react`. Same factory name as `permix/next` and `permix/express`: it returns a Permix instance plus isolated `PermixProvider`, `usePermix`, `Check`, and `PermixHydrate` bindings.
+
+The adapter supports React 18 and React 19. On React 19.2+ it uses native `useEffectEvent`; React 18 gets a compatible fallback. Peers are `react` / `react-dom` `>=18`.
Before getting started with React integration, make sure you've completed the
@@ -18,25 +20,42 @@ Permix provides official React integration through the `PermixProvider` componen
## Setup
-First, wrap your application with the `PermixProvider`:
+Call `createPermix` once at module scope:
+
+```ts title="lib/permix.ts"
+import { createPermix } from 'permix/react'
+
+export const { permix, PermixProvider, PermixHydrate, usePermix, Check } =
+ createPermix<{
+ post: ['create', 'read', { name: 'edit'; type: Post }]
+ }>()
+```
+
+Pass an existing core instance when you already created one (for example a client copy used with server hydration):
+
+```ts
+import { createPermix } from 'permix/react'
+import { clientPermix } from './client-core'
+
+export const { PermixProvider, PermixHydrate, usePermix, Check } =
+ createPermix(clientPermix)
+```
+
+Wrap the tree with the bound provider. It does not take a `permix` prop — the factory already closed over the instance:
```tsx title="App.tsx"
-import { PermixProvider } from 'permix/react'
-import { permix } from './lib/permix'
+import { PermixProvider } from './lib/permix'
function App() {
return (
-
+
)
}
```
-
- Remember to always pass the same Permix instance to both the `PermixProvider`
- and `usePermix` hook to maintain type safety.
-
+Each `createPermix` call gets its own React context, so nested factories (for example a posts policy and a comments policy) stay independent.
@@ -44,14 +63,22 @@ function App() {
## Hook
-For checking permissions in your components, you can use the `usePermix` hook. And to avoid importing the hook and Permix instance in every component, you can create a custom hook:
+`usePermix()` from the factory does not take an instance argument:
-```tsx title="hooks/use-permissions.ts"
-import { usePermix } from 'permix/react'
-import { permix } from '../lib/permix'
+```tsx title="page.tsx"
+import { usePermix } from './lib/permix'
-export function usePermissions() {
- return usePermix(permix)
+export default function Page() {
+ const post = usePost()
+ const { check, isReady } = usePermix()
+
+ if (!isReady) {
+ return Loading permissions...
+ }
+
+ const canEdit = check('post.edit', post)
+
+ return canEdit ? Edit Post : null
}
```
@@ -61,19 +88,11 @@ export function usePermissions() {
## Components
-If you prefer using components, you can import the `createComponents` function from `permix/react` and create checking components:
-
-```ts title="lib/permix.ts"
-import { createComponents } from 'permix/react'
-
-// ...
-
-export const { Check } = createComponents(permix)
-```
-
-And then you can use the `Check` component in your components:
+The factory also returns a typed `Check` component:
```tsx title="page.tsx"
+import { Check } from './lib/permix'
+
export default function Page() {
return (
-## Usage
-
-Use the `usePermix` hook and checking components in your components:
+## Hydration
-```tsx title="page.tsx"
-import { usePermix } from 'permix/react'
-import { permix } from './lib/permix'
-import { Check } from './lib/permix-components'
+For SSR, use the bound `PermixHydrate` to restore dehydrated server state on the client. `hydrate()` does not mark the instance ready and cannot restore function-based rules — call `setup()` on the client with the full rule set (usually in the same place you restore the session):
-export default function Page() {
- const post = usePost()
- const { check, isReady } = usePermix(permix)
-
- if (!isReady) {
- return Loading permissions...
- }
+```tsx title="App.tsx"
+import { useEffect } from 'react'
+import type { DehydratedState } from 'permix'
+import { permix, PermixHydrate, PermixProvider } from './lib/permix'
+import { getClientRules } from './lib/permissions'
- const canEdit = check('post.edit', post)
+function App({
+ dehydratedState,
+}: {
+ dehydratedState: DehydratedState<{ post: ['create', 'read'] }>
+}) {
+ useEffect(() => {
+ permix.setup(getClientRules())
+ }, [])
return (
-
- {canEdit ? (
-
Edit Post
- ) : (
-
You don't have permission to edit this post
- )}
-
- Can I create a post inside the Check component?
-
-
+
+
+
+
+
)
}
```
+See the [Hydration guide](/docs/guide/hydration) and framework-specific pages for [Next.js](/docs/integrations/next) and [TanStack Start](/docs/integrations/tanstack-start). `PermixHydrate` uses a dehydrated snapshot on the first render and synchronizes the instance after commit, so boolean checks work before client `setup()`. `isReady` stays `false` until you call `setup()` on the client.
+
-## Hydration
+## Compatible alternative
-For SSR applications, use `PermixHydrate` to restore dehydrated server state on the client. `hydrate()` does not mark the instance ready and cannot restore function-based rules — call `setup()` on the client with the full rule set (usually in the same place you restore the session):
+The previous exports still work: `PermixProvider` with a `permix` prop, `usePermix(permix)`, and `createComponents(permix)`. Always pass the **same** instance to the provider and the hook.
```tsx title="App.tsx"
-import { useEffect } from 'react'
-import type { DehydratedState } from 'permix'
-import { PermixHydrate, PermixProvider } from 'permix/react'
+import { PermixProvider, usePermix } from 'permix/react'
import { permix } from './lib/permix'
-import { getClientRules } from './lib/permissions'
-
-function App({ dehydratedState }: { dehydratedState: DehydratedState }) {
- useEffect(() => {
- permix.setup(getClientRules())
- }, [])
+function App() {
return (
-
-
-
+
)
}
+
+export function usePermissions() {
+ return usePermix(permix)
+}
+```
+
+```ts title="lib/permix-components.ts"
+import { createComponents } from 'permix/react'
+import { permix } from './lib/permix'
+
+export const { Check } = createComponents(permix)
```
-See the [Hydration guide](/docs/guide/hydration) and framework-specific pages for [Next.js](/docs/integrations/next) and [TanStack Start](/docs/integrations/tanstack-start).
+In development, `usePermix(permix)` throws if the instance does not match the provider.
diff --git a/docs/content/docs/integrations/tanstack-start.mdx b/docs/content/docs/integrations/tanstack-start.mdx
index fcf485f0..6f7b4945 100644
--- a/docs/content/docs/integrations/tanstack-start.mdx
+++ b/docs/content/docs/integrations/tanstack-start.mdx
@@ -409,7 +409,7 @@ export function EditButton({
}
```
-If you prefer the component API, create checkers with `createComponents` from `permix/react` — see the [React integration](/docs/integrations/react#components) for details.
+If you prefer the component API, use `Check` from `createPermix` in `permix/react` — see the [React integration](/docs/integrations/react).
diff --git a/examples/react/src/hooks/permissions.ts b/examples/react/src/hooks/permissions.ts
index 023cbb2f..278e231e 100644
--- a/examples/react/src/hooks/permissions.ts
+++ b/examples/react/src/hooks/permissions.ts
@@ -1,7 +1,5 @@
-import { usePermix } from 'permix/react'
-
-import { permix } from '../lib/permix'
+import { usePermix } from '../lib/permix'
export function usePermissions() {
- return usePermix(permix)
+ return usePermix()
}
diff --git a/examples/react/src/lib/permix.ts b/examples/react/src/lib/permix.ts
index 4bf596bf..734b6bfc 100644
--- a/examples/react/src/lib/permix.ts
+++ b/examples/react/src/lib/permix.ts
@@ -1,10 +1,9 @@
-import { createPermix } from 'permix'
-import { createComponents } from 'permix/react'
+import { createPermix } from 'permix/react'
import type { Post } from '../hooks/posts'
import type { User } from '../hooks/user'
-export const permix = createPermix<{
+export const { permix, PermixProvider, usePermix, Check } = createPermix<{
post: ['read', { name: 'edit'; type: Post }]
}>()
@@ -16,5 +15,3 @@ export function setupPermix(user: User) {
},
})
}
-
-export const { Check } = createComponents(permix)
diff --git a/examples/react/src/main.tsx b/examples/react/src/main.tsx
index 83689952..8de0784e 100644
--- a/examples/react/src/main.tsx
+++ b/examples/react/src/main.tsx
@@ -1,15 +1,14 @@
-import { PermixProvider } from 'permix/react'
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
-import { permix } from './lib/permix.ts'
+import { PermixProvider } from './lib/permix.ts'
import './index.css'
createRoot(document.querySelector('#root')!).render(
-
+
diff --git a/oxlint.config.ts b/oxlint.config.ts
index 56a74887..c56268e5 100644
--- a/oxlint.config.ts
+++ b/oxlint.config.ts
@@ -124,6 +124,14 @@ export default defineConfig({
'typescript/consistent-type-definitions': 'off',
},
},
+ {
+ files: ['permix/src/react/**'],
+ rules: {
+ 'typescript/no-explicit-any': 'error',
+ 'typescript/consistent-type-imports': 'error',
+ 'react/exhaustive-effect-dependencies': 'error',
+ },
+ },
{
files: nonReactGlobs,
rules: {
diff --git a/permix/skills/permix/SKILL.md b/permix/skills/permix/SKILL.md
index b3ebc1b5..33e5c7e9 100644
--- a/permix/skills/permix/SKILL.md
+++ b/permix/skills/permix/SKILL.md
@@ -37,7 +37,7 @@ Assumes a `permix` instance already exists (see **permix-getting-started**). Loa
| Task | Reference |
| --- | --- |
| `permix.check()` paths, callbacks, `~all`/`~any`, ReBAC/ABAC with entity data, `isReady` | [references/check.md](references/check.md) |
-| React, Vue, Solid, or Svelte UI — `PermixProvider`, `usePermix`, `createComponents`, SSR `dehydrate`/`hydrate` for Next.js / TanStack Start | [references/frontend.md](references/frontend.md) |
+| React, Vue, Solid, or Svelte UI — `createPermix` from `permix/react` (or `PermixProvider` / `usePermix` / `createComponents`), SSR `dehydrate`/`hydrate` for Next.js / TanStack Start | [references/frontend.md](references/frontend.md) |
| Protecting Express, Hono, Fastify, tRPC, oRPC, Node, or Elysia routes — `setupMiddleware`, `checkMiddleware` | [references/server.md](references/server.md) |
## Rules that apply everywhere
diff --git a/permix/skills/permix/references/frontend.md b/permix/skills/permix/references/frontend.md
index 510f619c..a6d1cb0f 100644
--- a/permix/skills/permix/references/frontend.md
+++ b/permix/skills/permix/references/frontend.md
@@ -6,21 +6,47 @@ Docs: https://permix.letstri.dev/docs/integrations/react
## React
-### Provider
+### Factory (recommended)
+
+Call `createPermix` from `permix/react` once at module scope — same name as `permix/next` and `permix/express`. It returns a Permix instance plus bound Provider, `usePermix`, `Check`, and `PermixHydrate` with an isolated context. Nested factories do not share state.
+
+```ts
+import { createPermix } from 'permix/react'
+
+export const { permix, PermixProvider, PermixHydrate, usePermix, Check } =
+ createPermix<{ post: ['create', 'read'] }>()
+```
```tsx
-import { PermixProvider } from 'permix/react'
-import { permix } from './lib/permix'
+import { PermixProvider, usePermix, Check } from './lib/permix'
export function App() {
return (
-
+
)
}
+
+function EditButton({ post }) {
+ const { check, isReady } = usePermix()
+
+ if (!isReady) return null
+
+ if (!check('post.update', post)) return null
+
+ return Edit
+}
+```
+
+```tsx
+Denied}>
+
+
```
+Supports React 18 and React 19. Native `useEffectEvent` is used on React 19.2+; React 18 uses a compatible fallback.
+
### Setup after auth
```ts
@@ -29,52 +55,33 @@ await loadUser()
permix.setup(roleRulesFor(user))
```
-### Hook (wrap once)
-
-```ts
-// hooks/use-permissions.ts
-import { usePermix } from 'permix/react'
-import { permix } from '../lib/permix'
+### Compatible alternative
-export function usePermissions() {
- return usePermix(permix)
-}
-```
+`PermixProvider` with a `permix` prop, `usePermix(permix)`, and `createComponents(permix)` still work. Pass the **same** `permix` instance to the provider and the hook — in development a mismatch throws.
```tsx
-function EditButton({ post }) {
- const { check, isReady } = usePermissions()
-
- if (!isReady) return null
+import { PermixProvider, usePermix } from 'permix/react'
+import { permix } from './lib/permix'
- if (!check('post.update', post)) return null
+export function App() {
+ return (
+
+
+
+ )
+}
- return Edit
+export function usePermissions() {
+ return usePermix(permix)
}
```
-Pass the **same** `permix` instance to `PermixProvider` and `usePermix`.
-
-### Declarative `Check` component
-
```ts
import { createComponents } from 'permix/react'
export const { Check } = createComponents(permix)
```
-```tsx
-Denied}>
-
-
-```
-
-```tsx
-
- Hidden when allowed; shown when denied
-
-```
-
### SSR
Use `PermixHydrate` + call `setup` again on the client for function rules — see **SSR and hydration** below.
@@ -169,15 +176,16 @@ Skipping client `setup` after hydrate leaves dynamic/ReBAC checks wrong.
### React
```tsx
-import { DehydratedState, PermixHydrate, PermixProvider } from 'permix/react'
+import type { DehydratedState } from 'permix'
+import { permix, PermixHydrate, PermixProvider } from './lib/permix'
function App({
dehydratedState,
}: {
- dehydratedState: DehydratedState
+ dehydratedState: DehydratedState<{ post: ['create', 'read'] }>
}) {
return (
-
+
@@ -186,12 +194,14 @@ function App({
}
```
-Run client `permix.setup(...)` where you restore the session (e.g. after `PermixHydrate` mounts or in the same auth effect).
+Run client `permix.setup(...)` where you restore the session (e.g. after `PermixHydrate` mounts or in the same auth effect). `PermixHydrate` supplies dehydrated booleans on the first render without mutating the instance during render; the instance hydrates after commit. `isReady` stays `false` until client `setup()`.
### Next.js / TanStack Start
Use framework helpers from `permix/next` or `permix/tanstack-start` when available — they wire dehydrate/hydrate into the framework data flow.
+**Next.js App Router (`permix/next`)** — `createPermix(resolveRules)` caches one initialized instance per request. Async Server Components `await getPermix()` / `await check()`. Non-async Server Components call `usePermix()` (React `use()`); `check()` is sync at the call site. Keep layouts/pages synchronous; put async permission/data work in feature components behind page-owned Suspense. Cookie/session checks stay out of the shared App Shell. `"use cache"` / `next/root-params` belong in the **app** resolver, not inside Permix. `"use cache: private"` payloads should check permission before loading data and return `null` when denied; `notFound()`/`redirect()` stay uncached. Client `check` is a UI hint. Route Handlers and Server Actions must `createPermix()` + `setup()` a core instance per invocation — they do not share RSC `cache()`.
+
In TanStack Start, `permix.get(context)` only works in server functions and server routes. To check inside `beforeLoad`/`loader`, put a core instance on the **router context** in `getRouter()` (`context: { permix }`), type it with `createRootRouteWithContext`, hydrate it in the root route's `beforeLoad`, then call `context.permix.check(...)` in any child route. Passing only the context type without the runtime value leaves `context.permix` undefined.
TanStack Start bundle caveat: if the `setupMiddleware()` callback imports server-only code (auth library, DB client, `node:` builtins), those imports leak into the client bundle — Start only strips `.server()` bodies it sees in app source, not the one hidden inside the library. Use `createMiddleware().server(permix.createSetupHandler(callback))` in `src/start.ts` instead; same behavior, but the boundary is visible to the compiler and the callback gets pruned.
diff --git a/permix/src/react/components.test.tsx b/permix/src/react/components.test.tsx
index 28a0050c..5b0c8716 100644
--- a/permix/src/react/components.test.tsx
+++ b/permix/src/react/components.test.tsx
@@ -1,10 +1,13 @@
import { render, waitFor } from '@testing-library/react'
+import * as React from 'react'
+// @ts-expect-error react-dom/server has no types under tsconfig.react.json `types: []`
+import { renderToString } from 'react-dom/server'
import { describe, expect, it } from 'vitest'
+import '@testing-library/jest-dom/vitest'
import { createPermix, PermixRuleNotDefinedError } from '../core'
import { createComponents, PermixHydrate, PermixProvider } from './components'
import { usePermix } from './hooks'
-import '@testing-library/jest-dom/vitest'
describe('components', () => {
it('should check hydration', async () => {
@@ -41,6 +44,150 @@ describe('components', () => {
expect(container.firstChild).toHaveTextContent('true')
})
+ it('uses dehydrated rules on the first render without mutating the instance', () => {
+ const permixServer = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ permixServer.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ const dehydrated = permixServer.dehydrate()
+ const permixClient = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ function TestComponent() {
+ const { check, isReady } = usePermix(permixClient)
+ return {`${check('post.create')}:${isReady}`}
+ }
+
+ const html = renderToString(
+
+
+
+
+
+ )
+
+ expect(html).toContain('true:false')
+ expect(permixClient.getRules()).toBeNull()
+ expect(permixClient.isReady()).toBe(false)
+ })
+
+ it('keeps isReady false after hydration until client setup', async () => {
+ const permixServer = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ permixServer.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ const dehydrated = permixServer.dehydrate()
+ const permixClient = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ function TestComponent() {
+ const { check, isReady } = usePermix(permixClient)
+ return (
+
+
+ {check('post.create').toString()}
+
+ {isReady.toString()}
+
+ )
+ }
+
+ const { getByTestId } = render(
+
+
+
+
+
+
+
+ )
+
+ expect(getByTestId('hydrate-create')).toHaveTextContent('true')
+ expect(getByTestId('hydrate-ready')).toHaveTextContent('false')
+
+ permixClient.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ await waitFor(() => {
+ expect(getByTestId('hydrate-ready')).toHaveTextContent('true')
+ })
+ })
+
+ it('replaces hydrated booleans when setup supplies new rules', async () => {
+ const permixServer = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ permixServer.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ const dehydrated = permixServer.dehydrate()
+ const permixClient = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ function TestComponent() {
+ const { check } = usePermix(permixClient)
+ return (
+
+
+ {check('post.create').toString()}
+
+
+ {check('post.read').toString()}
+
+
+ )
+ }
+
+ const { getByTestId } = render(
+
+
+
+
+
+ )
+
+ expect(getByTestId('replace-create')).toHaveTextContent('true')
+ expect(getByTestId('replace-read')).toHaveTextContent('false')
+
+ permixClient.setup({
+ post: {
+ create: false,
+ read: true,
+ },
+ })
+
+ await waitFor(() => {
+ expect(getByTestId('replace-create')).toHaveTextContent('false')
+ expect(getByTestId('replace-read')).toHaveTextContent('true')
+ })
+ })
+
it('should work with Check component', () => {
const permix = createPermix<{
post: ['create']
diff --git a/permix/src/react/components.tsx b/permix/src/react/components.tsx
index 097740df..3894b954 100644
--- a/permix/src/react/components.tsx
+++ b/permix/src/react/components.tsx
@@ -6,10 +6,34 @@ import type {
Definition,
DehydratedState,
Permix,
+ Rules,
RulesPaths,
} from '../core'
import type { PermixContext } from './hooks'
import { Context, usePermix, usePermixContext } from './hooks'
+import { useEffectEvent } from './use-effect-event'
+import { useLayoutEffect } from './use-isomorphic-layout-effect'
+
+function readPermixSnapshot(
+ permix: Permix
+): PermixContext {
+ return {
+ permix,
+ isReady: permix.isReady(),
+ rules: permix.getRules(),
+ }
+}
+
+function snapshotsEqual(
+ left: PermixContext,
+ right: PermixContext
+): boolean {
+ return (
+ left.permix === right.permix &&
+ left.isReady === right.isReady &&
+ left.rules === right.rules
+ )
+}
/**
* Provides Permix context to the React component tree.
@@ -19,56 +43,79 @@ import { Context, usePermix, usePermixContext } from './hooks'
export function PermixProvider({
children,
permix,
+ context,
}: {
children: React.ReactNode
permix: Permix
+ context?: React.Context | null>
}) {
- const [context, setContext] = React.useState>(() => ({
- permix,
- isReady: permix.isReady(),
- rules: permix.getRules(),
- }))
+ const Ctx = context ?? (Context as React.Context | null>)
+ const snapshotRef = React.useRef | null>(null)
- React.useEffect(() => {
- const syncRules = () => {
- queueMicrotask(() => {
- setContext((c) => ({ ...c, rules: permix.getRules() }))
- })
- }
- const syncReady = () => {
- queueMicrotask(() => {
- setContext((c) => ({ ...c, isReady: permix.isReady() }))
- })
- }
- const setup = permix.hook('setup', syncRules)
- const ready = permix.hook('ready', syncReady)
+ const subscribe = React.useCallback(
+ (onStoreChange: () => void) => {
+ const unsubSetup = permix.hook('setup', onStoreChange)
+ const unsubReady = permix.hook('ready', onStoreChange)
+ onStoreChange()
+ return () => {
+ unsubSetup()
+ unsubReady()
+ }
+ },
+ [permix]
+ )
- return () => {
- setup()
- ready()
+ const getSnapshot = React.useCallback(() => {
+ const next = readPermixSnapshot(permix)
+ const prev = snapshotRef.current
+ if (prev && snapshotsEqual(prev, next)) {
+ return prev
}
+ snapshotRef.current = next
+ return next
}, [permix])
- return {children}
+ const snapshot = React.useSyncExternalStore(
+ subscribe,
+ getSnapshot,
+ getSnapshot
+ )
+
+ return {children}
}
-export function PermixHydrate({
+export function PermixHydrate({
children,
state,
+ context,
}: {
children: React.ReactNode
- state: DehydratedState
+ state: DehydratedState
+ context?: React.Context | null>
}) {
- const { permix } = usePermixContext()
+ const Ctx = context ?? (Context as React.Context | null>)
+ const parent = usePermixContext(context)
+
+ const hydrateEvent = useEffectEvent((nextState: DehydratedState) => {
+ parent.permix.hydrate(nextState)
+ })
+
+ useLayoutEffect(() => {
+ hydrateEvent(state)
+ }, [state])
+
+ const overlay = React.useMemo>(
+ () => ({
+ permix: parent.permix,
+ isReady: parent.isReady,
+ rules: state as unknown as Rules,
+ }),
+ [parent.permix, parent.isReady, state]
+ )
- // Run before children render so `check()` can use hydrated booleans on the first pass.
- // PermixProvider defers context updates from the `setup` hook to avoid setState during render.
- // eslint-disable-next-line react/use-memo, react/void-use-memo
- React.useMemo(() => {
- permix.hydrate(state)
- }, [permix, state])
+ const value = parent.rules === null ? overlay : parent
- return children
+ return {children}
}
export interface CheckProps> {
@@ -84,7 +131,8 @@ export interface PermixComponents {
}
export function createComponents(
- permix: Pick, 'getRules' | 'check'>
+ permix: Pick, 'getRules' | 'check'>,
+ context?: React.Context | null>
): PermixComponents {
function Check>({
children,
@@ -93,7 +141,7 @@ export function createComponents(
otherwise = null,
reverse = false,
}: CheckProps) {
- const { check } = usePermix(permix)
+ const { check } = usePermix(permix, context)
const hasPermission = check(...([path, data] as unknown as CheckArgs))
return reverse
diff --git a/permix/src/react/create-permix.test.tsx b/permix/src/react/create-permix.test.tsx
new file mode 100644
index 00000000..8beea9b0
--- /dev/null
+++ b/permix/src/react/create-permix.test.tsx
@@ -0,0 +1,205 @@
+import { render, waitFor } from '@testing-library/react'
+import * as React from 'react'
+import { describe, expect, it } from 'vitest'
+import '@testing-library/jest-dom/vitest'
+
+import { createPermix as createCore } from '../core'
+import { createPermix } from './create-permix'
+import { PermixProvider, usePermix } from './index'
+
+describe('react factory contexts', () => {
+ it('creates a core instance and bound UI when called with a definition', () => {
+ const {
+ permix,
+ PermixProvider: BoundProvider,
+ usePermix: useBoundPermix,
+ Check,
+ } = createPermix<{
+ post: ['read']
+ }>()
+
+ permix.setup({
+ post: {
+ read: true,
+ },
+ })
+
+ function HookLabel() {
+ const { check, isReady } = useBoundPermix()
+ return (
+ {`${isReady}:${check('post.read')}`}
+ )
+ }
+
+ const { getByTestId, getByText } = render(
+
+
+
+ allowed
+
+
+ )
+
+ expect(getByTestId('hook')).toHaveTextContent('true:true')
+ expect(getByText('allowed')).toBeInTheDocument()
+ })
+
+ it('wraps an existing core instance', () => {
+ const permix = createCore<{
+ post: ['read']
+ }>()
+
+ permix.setup({
+ post: {
+ read: true,
+ },
+ })
+
+ const ui = createPermix(permix)
+
+ expect(ui.permix).toBe(permix)
+
+ function HookLabel() {
+ const { check } = ui.usePermix()
+ return {check('post.read').toString()}
+ }
+
+ const { getByTestId } = render(
+
+
+
+ )
+
+ expect(getByTestId('wrap')).toHaveTextContent('true')
+ })
+
+ it('keeps nested factory contexts independent', () => {
+ const postsUI = createPermix<{
+ post: ['read']
+ }>()
+ const commentsUI = createPermix<{
+ comment: ['read']
+ }>()
+
+ postsUI.permix.setup({
+ post: {
+ read: true,
+ },
+ })
+ commentsUI.permix.setup({
+ comment: {
+ read: false,
+ },
+ })
+
+ function Nested() {
+ const postsState = postsUI.usePermix()
+ const commentsState = commentsUI.usePermix()
+ return (
+
+
+ {postsState.check('post.read').toString()}
+
+
+ {commentsState.check('comment.read').toString()}
+
+
+ post-ok
+
+
+ comment-ok
+
+
+ )
+ }
+
+ const { getByTestId, queryByTestId } = render(
+
+
+
+
+
+ )
+
+ expect(getByTestId('post')).toHaveTextContent('true')
+ expect(getByTestId('comment')).toHaveTextContent('false')
+ expect(getByTestId('post-check')).toHaveTextContent('post-ok')
+ expect(queryByTestId('comment-check')).not.toBeInTheDocument()
+ })
+
+ it('hydrates through the factory overlay', async () => {
+ const server = createCore<{
+ post: ['create']
+ }>()
+ server.setup({
+ post: {
+ create: true,
+ },
+ })
+ const state = server.dehydrate()
+
+ const {
+ permix: client,
+ PermixProvider: BoundProvider,
+ PermixHydrate: BoundHydrate,
+ usePermix: useBoundPermix,
+ } = createPermix<{
+ post: ['create']
+ }>()
+
+ function Label() {
+ const { check, isReady } = useBoundPermix()
+ return (
+ {`${check('post.create')}:${isReady}`}
+ )
+ }
+
+ const { getByTestId } = render(
+
+
+
+
+
+ )
+
+ expect(getByTestId('hydrate')).toHaveTextContent('true:false')
+
+ client.setup({
+ post: {
+ create: true,
+ },
+ })
+
+ await waitFor(() => {
+ expect(getByTestId('hydrate')).toHaveTextContent('true:true')
+ })
+ })
+
+ it('throws in development when the singleton hook receives a different instance', () => {
+ const provided = createCore<{
+ post: ['read']
+ }>()
+ const other = createCore<{
+ post: ['read']
+ }>()
+
+ provided.setup({
+ post: {
+ read: true,
+ },
+ })
+
+ function Mismatch() {
+ const { check } = usePermix(other)
+ return {check('post.read').toString()}
+ }
+
+ expect(() =>
+ render(
+
+
+
+ )
+ ).toThrow(/same instance/)
+ })
+})
diff --git a/permix/src/react/create-permix.tsx b/permix/src/react/create-permix.tsx
new file mode 100644
index 00000000..9bb45790
--- /dev/null
+++ b/permix/src/react/create-permix.tsx
@@ -0,0 +1,75 @@
+import type { ReactNode } from 'react'
+
+import type { Definition, DehydratedState, Permix } from '../core'
+import { createPermix as createPermixCore } from '../core'
+import type { PermixComponents } from './components'
+import { createComponents, PermixHydrate, PermixProvider } from './components'
+import { createPermixContext, usePermix as usePermixFromContext } from './hooks'
+
+export interface CreatePermixResult {
+ permix: Permix
+ PermixProvider: (props: { children: ReactNode }) => ReactNode
+ PermixHydrate: (props: {
+ children: ReactNode
+ state: DehydratedState
+ }) => ReactNode
+ usePermix: () => {
+ check: Permix['check']
+ isReady: boolean
+ }
+ Check: PermixComponents['Check']
+}
+
+/**
+ * Create a Permix instance with isolated React bindings.
+ *
+ * Call once at module scope, same as `createPermix` from `permix/next` or
+ * `permix/express`. Pass an existing core instance to wrap it instead of
+ * creating a new one.
+ *
+ * @link https://permix.letstri.dev/docs/integrations/react
+ */
+export function createPermix(
+ instance?: Permix
+): CreatePermixResult {
+ const permix = instance ?? createPermixCore()
+ const context = createPermixContext()
+ const { Check } = createComponents(permix, context)
+
+ function BoundProvider({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ )
+ }
+
+ function BoundHydrate({
+ children,
+ state,
+ }: {
+ children: ReactNode
+ state: DehydratedState
+ }) {
+ return (
+
+ {children}
+
+ )
+ }
+
+ function useBoundPermix() {
+ return usePermixFromContext(permix, context)
+ }
+
+ BoundProvider.displayName = 'PermixProvider'
+ BoundHydrate.displayName = 'PermixHydrate'
+
+ return {
+ permix,
+ PermixProvider: BoundProvider,
+ PermixHydrate: BoundHydrate,
+ usePermix: useBoundPermix,
+ Check,
+ }
+}
diff --git a/permix/src/react/hooks.test.tsx b/permix/src/react/hooks.test.tsx
index b4c76628..7cb95cae 100644
--- a/permix/src/react/hooks.test.tsx
+++ b/permix/src/react/hooks.test.tsx
@@ -31,6 +31,37 @@ describe('permix react', () => {
expect(result.current.check('post.read')).toBe(false)
})
+ it('reads ready state on the first render when setup ran before subscribe', () => {
+ const permix = createPermix<{
+ post: ['create']
+ }>()
+
+ permix.setup({
+ post: {
+ create: true,
+ },
+ })
+
+ function TestComponent() {
+ const { isReady, check } = usePermix(permix)
+ return (
+
+ {isReady.toString()}:{check('post.create').toString()}
+
+ )
+ }
+
+ const { container } = render(
+
+
+
+
+
+ )
+
+ expect(container.firstChild).toHaveTextContent('true:true')
+ })
+
it('should work with DOM rerender', async () => {
const permix = createPermix<{
post: [{ name: 'create'; type: { id: string } }, 'read']
diff --git a/permix/src/react/hooks.ts b/permix/src/react/hooks.ts
index 720da34d..c779f2a8 100644
--- a/permix/src/react/hooks.ts
+++ b/permix/src/react/hooks.ts
@@ -9,18 +9,26 @@ export interface PermixContext {
rules: Rules | null
}
-export const Context = React.createContext>(null!)
+export function createPermixContext() {
+ return React.createContext | null>(null)
+}
+
+export const Context = createPermixContext()
-export function usePermixContext() {
- const context = React.useContext(Context)
+export function usePermixContext(
+ context?: React.Context | null>
+): PermixContext {
+ const value = React.useContext(
+ context ?? (Context as React.Context | null>)
+ )
- if (!context) {
+ if (!value) {
throw new Error(
'[Permix]: Looks like you forgot to wrap your app with '
)
}
- return context
+ return value
}
/**
@@ -29,15 +37,25 @@ export function usePermixContext() {
* @link https://permix.letstri.dev/docs/integrations/react
*/
export function usePermix(
- permix: Pick, 'getRules' | 'check'>
+ permix: Pick, 'getRules' | 'check'>,
+ context?: React.Context | null>
) {
- const { isReady, rules } = usePermixContext()
+ const { isReady, rules, permix: provided } = usePermixContext(context)
+
+ const nodeProcess = (
+ globalThis as typeof globalThis & {
+ process?: { env?: { NODE_ENV?: string } }
+ }
+ ).process
+
+ if (nodeProcess?.env?.NODE_ENV !== 'production' && provided !== permix) {
+ throw new Error(
+ '[Permix]: usePermix must receive the same instance passed to '
+ )
+ }
const check: Permix['check'] = React.useCallback(
- (...args) =>
- createCheck(() => (rules ?? permix.getRules()) as Rules | null)(
- ...args
- ),
+ (...args) => createCheck(() => rules ?? permix.getRules())(...args),
[rules, permix]
)
diff --git a/permix/src/react/index.ts b/permix/src/react/index.ts
index 0852278f..3c1ae27e 100644
--- a/permix/src/react/index.ts
+++ b/permix/src/react/index.ts
@@ -1,4 +1,7 @@
export { createComponents, PermixHydrate, PermixProvider } from './components'
export type { CheckProps, PermixComponents } from './components'
+export { createPermix } from './create-permix'
+export type { CreatePermixResult } from './create-permix'
export { usePermix } from './hooks'
export type { PermixContext } from './hooks'
+
diff --git a/permix/src/react/use-effect-event-compat.test.tsx b/permix/src/react/use-effect-event-compat.test.tsx
new file mode 100644
index 00000000..639ae5f1
--- /dev/null
+++ b/permix/src/react/use-effect-event-compat.test.tsx
@@ -0,0 +1,29 @@
+import { renderHook } from '@testing-library/react'
+import * as React from 'react'
+import { describe, expect, it } from 'vitest'
+
+import { useEffectEvent } from './use-effect-event'
+
+describe('useEffectEvent compatibility', () => {
+ it('keeps a stable identity while reading the latest callback', () => {
+ const calls: string[] = []
+ const { rerender } = renderHook(
+ ({ value, prefix }: { value: string; prefix: string }) => {
+ const onValue = useEffectEvent((next: string) => {
+ calls.push(`${prefix}:${next}`)
+ })
+
+ React.useEffect(() => {
+ onValue(value)
+ }, [value])
+
+ return onValue
+ },
+ { initialProps: { value: 'light', prefix: 'initial' } }
+ )
+
+ rerender({ value: 'dark', prefix: 'latest' })
+
+ expect(calls).toStrictEqual(['initial:light', 'latest:dark'])
+ })
+})
diff --git a/permix/src/react/use-effect-event.ts b/permix/src/react/use-effect-event.ts
new file mode 100644
index 00000000..9007be60
--- /dev/null
+++ b/permix/src/react/use-effect-event.ts
@@ -0,0 +1,29 @@
+import * as React from 'react'
+
+type EffectEventHook = (
+ callback: (...arguments_: Arguments) => Result
+) => (...arguments_: Arguments) => Result
+
+function useEffectEventFallback(
+ callback: (...arguments_: Arguments) => Result
+): (...arguments_: Arguments) => Result {
+ const callbackRef = React.useRef(callback)
+
+ React.useInsertionEffect(() => {
+ callbackRef.current = callback
+ }, [callback])
+
+ return React.useCallback(
+ (...arguments_: Arguments) => callbackRef.current(...arguments_),
+ []
+ )
+}
+
+const nativeUseEffectEvent = (
+ React as typeof React & {
+ useEffectEvent?: EffectEventHook
+ }
+).useEffectEvent
+
+export const useEffectEvent: EffectEventHook =
+ nativeUseEffectEvent ?? useEffectEventFallback
diff --git a/permix/src/react/use-isomorphic-layout-effect.ts b/permix/src/react/use-isomorphic-layout-effect.ts
new file mode 100644
index 00000000..e91de35e
--- /dev/null
+++ b/permix/src/react/use-isomorphic-layout-effect.ts
@@ -0,0 +1,4 @@
+import * as React from 'react'
+
+export const useLayoutEffect =
+ 'window' in globalThis ? React.useLayoutEffect : React.useEffect
diff --git a/permix/test-d/public-api.ts b/permix/test-d/public-api.ts
index 2bbcaf9d..19108ac1 100644
--- a/permix/test-d/public-api.ts
+++ b/permix/test-d/public-api.ts
@@ -10,7 +10,10 @@ import { createPermix as createHonoPermix } from 'permix/hono'
import { createPermix as createNextPermix } from 'permix/next'
import { createPermix as createNodePermix } from 'permix/node'
import { createPermix as createOrpcPermix } from 'permix/orpc'
-import { createComponents as createReactComponents } from 'permix/react'
+import {
+ createComponents as createReactComponents,
+ createPermix as createReactPermix,
+} from 'permix/react'
import { createPermix as createServerPermix } from 'permix/server'
import { createComponents as createSolidComponents } from 'permix/solid'
import { createComponents as createSvelteComponents } from 'permix/svelte'
@@ -24,6 +27,8 @@ type PostDefinition = {
const core = createPermix()
const react = createReactComponents(core)
+const reactFactory = createReactPermix(core)
+const reactStandalone = createReactPermix()
const vue = createVueComponents(core)
const trpc = createTrpcPermix()
const orpc = createOrpcPermix()
@@ -50,25 +55,30 @@ core.setup({
const checkArgs: CheckArgs = ['post.create']
const allowed = core.check(...checkArgs)
+type FactoryCheck = ReturnType<(typeof reactFactory)['usePermix']>['check']
+const factoryCheck: FactoryCheck = core.check
export {
- allowed,
- core,
- drizzle,
- drizzleLegacy,
- effect,
- elysia,
- express,
- fastify,
- hono,
- next,
- node,
- orpc,
- react,
- server,
- solid,
- svelte,
- tanstackStart,
- trpc,
- vue,
+ allowed,
+ core,
+ drizzle,
+ drizzleLegacy,
+ effect,
+ elysia,
+ express,
+ factoryCheck,
+ fastify,
+ hono,
+ next,
+ node,
+ orpc,
+ react,
+ reactFactory,
+ reactStandalone,
+ server,
+ solid,
+ svelte,
+ tanstackStart,
+ trpc,
+ vue
}
diff --git a/permix/tsconfig.compat.json b/permix/tsconfig.compat.json
index 292210be..f373133c 100644
--- a/permix/tsconfig.compat.json
+++ b/permix/tsconfig.compat.json
@@ -7,12 +7,19 @@
"moduleDetection": "force",
"strict": true,
"exactOptionalPropertyTypes": true,
- "noEmit": true,
+ "noImplicitOverride": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noUncheckedSideEffectImports": true,
+ "allowUnreachableCode": false,
+ "allowUnusedLabels": false,
+ "verbatimModuleSyntax": true,
+ "isolatedModules": true,
+ "erasableSyntaxOnly": true,
+ "noEmit": true,
"skipLibCheck": true,
- "types": [],
- "verbatimModuleSyntax": true
+ "types": []
},
"include": ["test-d/**/*.ts"]
}
From ea49d790d01a67c87eaf8daa41987c78c37f4d85 Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 13:05:10 +0200
Subject: [PATCH 03/28] feat: initialize Next.js Permix from a request-scoped
rules resolver
Cache one initialized instance per RSC request so concurrent server callers share setup, and keep layouts synchronous with permission work behind Suspense for Cache Components.
Co-authored-by: Cursor
---
.github/workflows/next-integration.yml | 36 +
.gitignore | 3 +
docs/content/docs/integrations/next.mdx | 349 +++----
docs/content/docs/migration-v3-to-v4.mdx | 18 +-
examples/next/app/actions.ts | 15 +
examples/next/app/api/posts/route.ts | 9 +-
examples/next/app/features/post-list.tsx | 75 ++
examples/next/app/features/private-edit.tsx | 43 +
.../next/app/features/public-read-badge.tsx | 12 +
examples/next/app/features/server-checks.tsx | 28 +
examples/next/app/features/session-panel.tsx | 27 +
.../next/app/features/sync-read-badge.tsx | 14 +
examples/next/app/layout.tsx | 30 +-
examples/next/app/page.tsx | 106 +-
examples/next/app/posts/[id]/edit-button.tsx | 8 +-
examples/next/app/posts/[id]/page.tsx | 75 +-
examples/next/app/providers.tsx | 25 +-
examples/next/lib/client-permix.ts | 11 +
examples/next/lib/permissions.ts | 51 +
examples/next/lib/permix.ts | 32 +-
examples/next/next.config.ts | 8 +
examples/next/package.json | 2 +-
ignores.ts | 4 +
package.json | 1 +
permix/package.json | 2 +-
permix/src/next/hydration.test.tsx | 31 +-
permix/src/next/permix.test.ts | 196 ++--
permix/src/next/permix.ts | 110 +-
permix/src/next/request-cache-mock.ts | 34 +
permix/test-d/public-api.ts | 8 +-
permix/test/next/.gitignore | 4 +
permix/test/next/instant-nav.rig.md | 10 +
permix/test/next/package.json | 25 +
permix/test/next/playwright.config.ts | 36 +
permix/test/next/run.mjs | 183 ++++
.../src-ppr/app/[tenant]/dashboard/page.tsx | 10 +
.../test/next/src-ppr/app/[tenant]/layout.tsx | 59 ++
.../test/next/src-ppr/app/[tenant]/page.tsx | 44 +
.../src-ppr/app/features/cache-safe-check.tsx | 13 +
.../src-ppr/app/features/permission-holes.tsx | 21 +
.../src-ppr/app/features/private-edit.tsx | 24 +
.../src-ppr/app/features/session-island.tsx | 13 +
permix/test/next/src-ppr/lib/tenant-permix.ts | 15 +
permix/test/next/src-ppr/next.config.mjs | 18 +
permix/test/next/src-ppr/root-params.d.ts | 3 +
permix/test/next/src/app/actions.ts | 13 +
permix/test/next/src/app/api/check/route.ts | 14 +
.../next/src/app/features/action-check.tsx | 25 +
.../src/app/features/concurrent-instances.tsx | 16 +
.../next/src/app/features/public-read.tsx | 6 +
.../next/src/app/features/session-create.tsx | 8 +
.../src/app/features/use-permix-create.tsx | 10 +
permix/test/next/src/app/layout.tsx | 16 +
permix/test/next/src/app/page.tsx | 33 +
permix/test/next/src/lib/auth.ts | 9 +
permix/test/next/src/lib/instance-id.ts | 12 +
permix/test/next/src/lib/permix.ts | 29 +
permix/test/next/src/next-env.d.ts | 2 +
permix/test/next/src/next.config.mjs | 12 +
permix/test/next/src/package.json | 5 +
permix/test/next/src/tsconfig.json | 21 +
permix/test/next/tests/compat.spec.ts | 98 ++
permix/test/next/tests/instant.spec.ts | 103 ++
permix/vitest.config.ts | 7 +-
pnpm-lock.yaml | 969 ++++++++++++++++--
pnpm-workspace.yaml | 1 +
66 files changed, 2642 insertions(+), 608 deletions(-)
create mode 100644 .github/workflows/next-integration.yml
create mode 100644 examples/next/app/features/post-list.tsx
create mode 100644 examples/next/app/features/private-edit.tsx
create mode 100644 examples/next/app/features/public-read-badge.tsx
create mode 100644 examples/next/app/features/server-checks.tsx
create mode 100644 examples/next/app/features/session-panel.tsx
create mode 100644 examples/next/app/features/sync-read-badge.tsx
create mode 100644 examples/next/lib/client-permix.ts
create mode 100644 examples/next/lib/permissions.ts
create mode 100644 permix/src/next/request-cache-mock.ts
create mode 100644 permix/test/next/.gitignore
create mode 100644 permix/test/next/instant-nav.rig.md
create mode 100644 permix/test/next/package.json
create mode 100644 permix/test/next/playwright.config.ts
create mode 100644 permix/test/next/run.mjs
create mode 100644 permix/test/next/src-ppr/app/[tenant]/dashboard/page.tsx
create mode 100644 permix/test/next/src-ppr/app/[tenant]/layout.tsx
create mode 100644 permix/test/next/src-ppr/app/[tenant]/page.tsx
create mode 100644 permix/test/next/src-ppr/app/features/cache-safe-check.tsx
create mode 100644 permix/test/next/src-ppr/app/features/permission-holes.tsx
create mode 100644 permix/test/next/src-ppr/app/features/private-edit.tsx
create mode 100644 permix/test/next/src-ppr/app/features/session-island.tsx
create mode 100644 permix/test/next/src-ppr/lib/tenant-permix.ts
create mode 100644 permix/test/next/src-ppr/next.config.mjs
create mode 100644 permix/test/next/src-ppr/root-params.d.ts
create mode 100644 permix/test/next/src/app/actions.ts
create mode 100644 permix/test/next/src/app/api/check/route.ts
create mode 100644 permix/test/next/src/app/features/action-check.tsx
create mode 100644 permix/test/next/src/app/features/concurrent-instances.tsx
create mode 100644 permix/test/next/src/app/features/public-read.tsx
create mode 100644 permix/test/next/src/app/features/session-create.tsx
create mode 100644 permix/test/next/src/app/features/use-permix-create.tsx
create mode 100644 permix/test/next/src/app/layout.tsx
create mode 100644 permix/test/next/src/app/page.tsx
create mode 100644 permix/test/next/src/lib/auth.ts
create mode 100644 permix/test/next/src/lib/instance-id.ts
create mode 100644 permix/test/next/src/lib/permix.ts
create mode 100644 permix/test/next/src/next-env.d.ts
create mode 100644 permix/test/next/src/next.config.mjs
create mode 100644 permix/test/next/src/package.json
create mode 100644 permix/test/next/src/tsconfig.json
create mode 100644 permix/test/next/tests/compat.spec.ts
create mode 100644 permix/test/next/tests/instant.spec.ts
diff --git a/.github/workflows/next-integration.yml b/.github/workflows/next-integration.yml
new file mode 100644
index 00000000..809c956d
--- /dev/null
+++ b/.github/workflows/next-integration.yml
@@ -0,0 +1,36 @@
+name: Next Integration
+
+on:
+ pull_request:
+ types: [opened, synchronize]
+ branches:
+ - main
+
+jobs:
+ next-matrix:
+ runs-on: ubuntu-latest
+ timeout-minutes: 45
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 24
+ cache: pnpm
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Install Playwright Chromium
+ run: pnpm --filter @permix/next-integration exec playwright install chromium --with-deps
+
+ - name: Build Permix and run Next fixtures
+ run: pnpm test:next
diff --git a/.gitignore b/.gitignore
index 5d09cfd3..586c06bb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,6 @@ node_modules
.DS_Store
.pnpm-store
.turbo
+permix/test/next/.scratch
+permix/test/next/playwright-report
+permix/test/next/test-results
diff --git a/docs/content/docs/integrations/next.mdx b/docs/content/docs/integrations/next.mdx
index 5f1f1204..c0f4ffc3 100644
--- a/docs/content/docs/integrations/next.mdx
+++ b/docs/content/docs/integrations/next.mdx
@@ -5,244 +5,242 @@ description: Learn how to use Permix with Next.js App Router
## Overview
-Permix provides a dedicated integration for the Next.js **App Router** through `permix/next`. It exposes a `createPermix` factory that returns a **per-request** Permix instance backed by React's [`cache()`](https://react.dev/reference/react/cache), so you can `setup()` the rules once and `check()` them anywhere on the server — layouts, pages, route handlers, and server actions — without threading the instance through props.
+`permix/next` is a request-safe helper for the Next.js **App Router**. You pass a rules resolver into `createPermix`. React's [`cache()`](https://react.dev/reference/react/cache) memoizes **one Promise** of a fully initialized core instance per request, so concurrent Server Components share the same setup instead of racing layout mutation.
-The client side reuses the existing React integration (`permix/react`): the server `dehydrate()`s its state and the client hydrates it into its own singleton via `PermixProvider` + `PermixHydrate`.
+The facade exposes **dual access** to that Promise:
+
+- Async `getPermix` / `check` / `getRules` / `dehydrate` for async Server Components
+- `usePermix()` for non-async Server Components — it unwraps the same Promise with React [`use()`](https://react.dev/reference/react/use). `check()` stays synchronous at the call site; React may suspend only while rules resolve
+
+Sync vs async is an **API** choice, not the PPR boundary. A permission UI can prerender after either path when its policy inputs are static or cached. User, session, or cookie-dependent outcomes must stay behind Suspense and must not enter a shared build-time shell.
+
+The client still uses `permix/react`. Hydrate only the permission-dependent islands — do not block the static App Shell on dehydrated state.
- Before getting started with the Next.js integration, make sure you've
- completed the initial setup steps in the [Quick Start](/docs/quick-start)
- guide. Familiarity with the [Hydration guide](/docs/guide/hydration) helps
- too.
+ Complete the [Quick Start](/docs/quick-start) and read the [Hydration
+ guide](/docs/guide/hydration) before this page.
- This integration is designed for the **App Router**. It relies on `react`'s
- request-scoped `cache()`, which is available in server components, route
- handlers, and server actions within a single request.
+ Requires **Next.js 15+**. Route Handlers and Server Actions do **not** share
+ the RSC `cache()` identity. Create and `setup()` an explicit core
+ `createPermix()` instance inside each invocation.
-## Define your permissions
-
-Create a Permix instance once in a shared module so it can be imported anywhere on the server:
+## Define the resolver
```ts title="lib/permix.ts"
import { createPermix } from 'permix/next'
-
-interface Post {
- id: string
- authorId: string
-}
+import { getSession } from '@/lib/auth'
export const permix = createPermix<{
post: [
- { name: 'create'; type: Post },
- { name: 'read'; type: Post },
- { name: 'update'; type: Post },
- { name: 'delete'; type: Post },
+ { name: 'create'; type: { id: string; authorId: string } },
+ { name: 'read'; type: { id: string; authorId: string } },
+ { name: 'update'; type: { id: string; authorId: string } },
+ { name: 'delete'; type: { id: string; authorId: string } },
]
-}>()
-```
-
-The returned helper does **not** hold any permission state at module scope — every request gets its own isolated instance.
-
-
-
-
-
-## Setup per request
-
-Call `setup()` early in the request lifecycle. A common place is the root layout (or any server component that runs before the ones doing `check`s). Resolve any async data (session, headers, cookies, DB lookups) first, then pass plain rules to `setup`:
-
-```tsx title="app/layout.tsx"
-import { permix } from '@/lib/permix'
-import { getSession } from '@/lib/auth'
-
-export default async function RootLayout({
- children,
-}: {
- children: React.ReactNode
-}) {
+}>(async () => {
const session = await getSession()
-
- permix.setup({
+ return {
post: {
create: !!session,
read: true,
update: (post) => post?.authorId === session?.userId,
delete: session?.role === 'admin',
},
- })
-
- return (
-
- {children}
-
- )
-}
+ }
+})
```
-
- Because `setup()` is scoped to the current request, calling it again from a
- nested server component or route handler in the **same** request simply
- replaces the rules for that request. Other requests are unaffected.
-
+The helper holds no permission state at module scope. The first caller in a request starts initialization; every other caller awaits the same Promise.
+
+Keep layouts and pages **synchronous**. Move async permission and data work into feature-owned Server Components, and put shaped skeletons behind **page-owned** Suspense so static chrome remains in the App Shell.
-## Check on the server
-
-Anywhere a server component, route handler, or server action runs in that request, you can use `check()`:
+## Async Server Components
```tsx title="app/posts/[id]/page.tsx"
import { notFound } from 'next/navigation'
+import { Suspense } from 'react'
import { permix } from '@/lib/permix'
import { getPost } from '@/lib/posts'
-export default async function PostPage({
+export default function PostPage({
params,
}: {
params: Promise<{ id: string }>
}) {
- const { id } = await params
- const post = await getPost(id)
+ return (
+
+ Post
+ }>
+ {params.then(({ id }) => (
+
+ ))}
+
+
+ )
+}
- if (!permix.check('post.read', post)) {
+async function PostArticle({ id }: { id: string }) {
+ const post = await getPost(id)
+ if (!post || !(await permix.check('post.read', post))) {
notFound()
}
-
- return {/* ... */}
+ return {post.id}
}
```
-```ts title="app/api/posts/route.ts"
+`await permix.getPermix()` returns the initialized core instance when you need `isReady()` or multiple synchronous `check()` calls after the await.
+
+
+
+
+
+## Non-async Server Components
+
+```tsx title="app/features/sync-read-badge.tsx"
import { permix } from '@/lib/permix'
-export async function POST(req: Request) {
- if (!permix.check('post.create')) {
- return Response.json({ error: 'Forbidden' }, { status: 403 })
+export function SyncReadBadge() {
+ const instance = permix.usePermix()
+ return {instance.check('post.read') ? 'allowed' : 'denied'}
+}
+```
+
+Place this behind Suspense. `usePermix()` from `permix/next` is for Server Components. Client components keep using `usePermix` from `permix/react`.
+
+
+
+
+
+## Prerender-safe vs request-specific rules
+
+| Outcome | When | Where it lives |
+| --- | --- | --- |
+| Globally cacheable check | Policy inputs are static or `"use cache"` | Shared App Shell |
+| Root-param-keyed check | URL is known (`next/root-params` in **your** resolver); optional `prefetch={true}` | Per-link prefetch |
+| Warm `"use cache: private"` payload | Same session already cached/prefetched | Authorized content can commit immediately |
+| Cold or deliberately fresh session check | Cookie/session/user data, uncached | Dynamic hole behind Suspense |
+
+Permix does not import generated root-param getters. For tenant or locale policies, read `next/root-params` in the **app-owned** resolver (the same pattern as next-intl). Cache serializable policy inputs with `"use cache"`, then build any function rules outside that cache boundary.
+
+Keep ordinary tenant/resource slugs as page values passed through `params.then(...)`. Do not promote a nested slug to a root param solely to avoid prop passing.
+
+
+
+
+
+## Permission-first private payloads
+
+Check permission **before** loading feature data. Return serializable data or `null` when denied. An uncached caller performs `notFound()` / `redirect()` — denial control flow stays outside the cached payload. Cache tags remain app-owned; Permix cannot infer a feature's data or membership lifecycle.
+
+```tsx title="app/features/private-edit.tsx"
+import { createPermix } from 'permix'
+import { getSession } from '@/lib/auth'
+import { rulesForSession, type PermissionsDefinition } from '@/lib/permissions'
+import { getPost } from '@/lib/posts'
+
+async function readPostUpdatePayload(postId: string) {
+ 'use cache: private'
+ const session = await getSession()
+ const permix = createPermix()
+ permix.setup(rulesForSession(session))
+ const post = await getPost(postId)
+ if (!post || !permix.check('post.update', post)) {
+ return null
}
+ return { id: post.id, authorId: post.authorId }
+}
- // create the post...
- return Response.json({ ok: true })
+export async function PrivateEditIsland({ postId }: { postId: string }) {
+ const payload = await readPostUpdatePayload(postId)
+ if (!payload) {
+ return null
+ }
+ return
}
```
-You can also reach the underlying core instance through `permix.get()` if you need methods like `isReady()` or `getRules()`.
+A client `check()` after hydrate is a **UI hint**, not enforcement. Authoritative decisions stay on the server (this payload, Route Handlers, Server Actions).
-## Send permissions to the client
+## Hydrate permission-dependent UI only
+
+Create the client bindings with `createPermix` from `permix/react` — same factory name as the server adapter, different import path:
-Use `dehydrate()` to serialize the request's permissions and hand them to a client provider. The server cannot send the Permix instance itself across the boundary — only the JSON state.
+```ts title="lib/client-permix.ts"
+import { createPermix } from 'permix/react'
+import type { PermissionsDefinition } from './permissions'
+
+export const {
+ permix: clientPermix,
+ PermixProvider,
+ PermixHydrate,
+ usePermix,
+ Check,
+} = createPermix()
+```
```tsx title="app/providers.tsx"
'use client'
-import { createPermix } from 'permix'
-import { PermixHydrate, PermixProvider } from 'permix/react'
import type { DehydratedState } from 'permix'
-
-// One singleton per browser tab. The same type definition as on the server.
-const permix = createPermix<{
- post: [
- { name: 'create'; type: { id: string; authorId: string } },
- { name: 'read'; type: { id: string; authorId: string } },
- { name: 'update'; type: { id: string; authorId: string } },
- { name: 'delete'; type: { id: string; authorId: string } },
- ]
-}>()
+import { PermixHydrate, PermixProvider } from '@/lib/client-permix'
+import type { PermissionsDefinition } from '@/lib/permissions'
export function Providers({
state,
children,
}: {
- state: DehydratedState
+ state: DehydratedState
children: React.ReactNode
}) {
return (
-
+
{children}
)
}
-
-export { permix }
```
-Then wire it up in your root layout right after `setup()`:
-
-```tsx title="app/layout.tsx"
-import { permix } from '@/lib/permix'
-import { getSession } from '@/lib/auth'
-import { Providers } from './providers'
-
-export default async function RootLayout({
- children,
-}: {
- children: React.ReactNode
-}) {
- const session = await getSession()
-
- permix.setup({/* ...rules derived from session... */})
-
- return (
-
-
- {children}
-
-
- )
-}
-```
-
-
- `hydrate()` restores the boolean state but does not flip `isReady` on its own
- — function-based rules are lost during serialization. If you need `isReady` on
- the client (e.g. to gate UI on `usePermix(...).isReady`), call
- `permix.setup(...)` on the client too with the same shape (using booleans and
- any function rules you want active client-side). See the [Hydration
- guide](/docs/guide/hydration) for details.
-
+Await `permix.dehydrate()` in the feature that owns the island, not in the root layout. `hydrate()` restores booleans and does not set `isReady`. Call `clientPermix.setup(...)` on the client for function rules — see [Hydration](/docs/guide/hydration).
-## Use on the client
-
-From any client component, import the singleton from `app/providers.tsx` and the hooks/components from `permix/react`:
+## Route Handlers and Server Actions
-```tsx title="app/posts/[id]/edit-button.tsx"
-'use client'
-
-import { usePermix } from 'permix/react'
-import { permix } from '@/app/providers'
+```ts title="app/api/posts/route.ts"
+import { createPermix } from 'permix'
+import { getSession } from '@/lib/auth'
+import { rulesForSession, type PermissionsDefinition } from '@/lib/permissions'
-export function EditButton({
- post,
-}: {
- post: { id: string; authorId: string }
-}) {
- const { check } = usePermix(permix)
+export async function POST() {
+ const permix = createPermix()
+ permix.setup(rulesForSession(await getSession()))
- if (!check('post.update', post)) {
- return null
+ if (!permix.check('post.create')) {
+ return Response.json({ error: 'Forbidden' }, { status: 403 })
}
- return Edit post
+ return Response.json({ ok: true })
}
```
-If you prefer the component API, create checkers with `createComponents` from `permix/react` and use them in your client components — see the [React integration](/docs/integrations/react#components) for details.
+Share `rulesForSession` (or templates) with the RSC resolver so the policy stays in one place.
@@ -250,32 +248,15 @@ If you prefer the component API, create checkers with `createComponents` from `p
## Templates
-`createPermix` exposes the same `template()` helper as the core API for reusing rule sets:
+`template()` does not wait on initialization:
```ts title="lib/permix.ts"
-import { createPermix } from 'permix/next'
-
-export const permix = createPermix<{
- post: ['create', 'read', 'update', 'delete']
-}>()
-
export const adminTemplate = permix.template({
post: { create: true, read: true, update: true, delete: true },
})
-
-export const guestTemplate = permix.template({
- post: { create: false, read: true, update: false, delete: false },
-})
```
-```tsx title="app/layout.tsx"
-import { permix, adminTemplate, guestTemplate } from '@/lib/permix'
-import { getSession } from '@/lib/auth'
-
-const session = await getSession()
-
-permix.setup(session?.role === 'admin' ? adminTemplate() : guestTemplate())
-```
+You can also use `createTemplate` from `permix` in a shared module imported by the resolver, handlers, and the client.
@@ -283,47 +264,29 @@ permix.setup(session?.role === 'admin' ? adminTemplate() : guestTemplate())
## Example
-You can find a runnable example of the Next.js integration [here](https://github.com/letstri/permix/tree/main/examples/next).
+Runnable App Router example with Cache Components and Partial Prefetching: [examples/next](https://github.com/letstri/permix/tree/main/examples/next).
-## How per-request isolation works
-
-`createPermix` from `permix/next` wraps a single core instance per request using React's `cache()`. Inside one Next.js request:
-
-- The first call to `setup`/`check`/`get`/`dehydrate` creates (or reuses) **one** instance.
-- All subsequent calls in the same request — across server components, route handlers, and server actions — share that instance.
-
-Across concurrent requests, each request gets its **own** instance. State never leaks between users.
-
-
- Do **not** store the result of `permix.get()` (or any rule data) in
- module-level variables. That would defeat per-request isolation. Always go
- through `permix.check()` / `permix.get()` so the request-scoped cache is
- consulted.
-
-
## API
-### `createPermix()`
+### `createPermix(resolveRules)`
-Returns an object with the following methods:
+`resolveRules` is `() => Rules | Promise>`.
| Method | Description |
| --- | --- |
-| `setup(rules)` | Set the per-request permission rules. Resolve any async data (session, etc.) before calling. |
-| `check(...args)` | Check a permission against the current request's rules. Same signature as the core `check`. |
-| `get()` | Return the underlying [`Permix`](/docs/guide/instance) instance for the current request. |
-| `getRules()` | Return the current rules object for the request-scoped instance, or `null`. |
-| `dehydrate()` | Serialize the current request's rules to JSON (for `` on the client). |
-| `template(rules)` | Create a reusable rule set. Same as the core [`template`](/docs/guide/template). |
+| `getPermix()` | Promise of the initialized [`Permix`](/docs/guide/instance) for this request |
+| `usePermix()` | Same instance via React `use()` (Server Components) |
+| `check(...args)` | Async check against the initialized instance |
+| `getRules()` | Async current rules, or `null` |
+| `dehydrate()` | Async JSON snapshot for `` |
+| `template(rules)` | Reusable rule set — same as core [`template`](/docs/guide/template) |
-## TanStack Start and other frameworks
+There is no `setup`, `hook`, or `hookOnce` on the Next facade. Initialization is the resolver. Subscribe to core hooks on the instance from `getPermix()` / `usePermix()` if you need them.
-The client layer (`permix/react`) is framework-agnostic. If you're using TanStack Start, Remix, or a custom React SSR setup, you can still use `permix/react` on the client. For the server side, either:
+## TanStack Start and other frameworks
-- Use the dedicated [`permix/tanstack-start`](/docs/integrations/tanstack-start) integration, which follows the same shape as `permix/next`, or
-- Create a core Permix instance per request manually (see [Hydration guide](/docs/guide/hydration)), or
-- Use an existing server integration like [`permix/node`](/docs/integrations/node), [`permix/express`](/docs/integrations/express), or [`permix/hono`](/docs/integrations/hono) when applicable.
+The client layer (`permix/react`) is framework-agnostic. For TanStack Start use [`permix/tanstack-start`](/docs/integrations/tanstack-start). For other servers, create a core instance per request or use [`permix/node`](/docs/integrations/node), [`permix/express`](/docs/integrations/express), or [`permix/hono`](/docs/integrations/hono).
diff --git a/docs/content/docs/migration-v3-to-v4.mdx b/docs/content/docs/migration-v3-to-v4.mdx
index 7918de34..b5b896b9 100644
--- a/docs/content/docs/migration-v3-to-v4.mdx
+++ b/docs/content/docs/migration-v3-to-v4.mdx
@@ -336,15 +336,15 @@ Map each Better Auth role to a rules object yourself — the same booleans you p
These are additive — migrate the core API first, then adopt what you need:
-| Import | Use case |
-| ----------------------- | ------------------------------------------- |
-| `permix/next` | Next.js App Router, request-scoped instance |
-| `permix/tanstack-start` | TanStack Start middleware and SSR |
-| `permix/server` | Framework-agnostic fetch middleware |
-| `permix/svelte` | Svelte 5 runes |
-| `permix/drizzle` | Rules from Drizzle v1 schema |
-| `permix/drizzle/legacy` | Drizzle v0 (`>=0.30 <1`) |
-| `permix/effect` | Effect `Layer` / `Context` |
+| Import | Use case |
+| ----------------------- | ---------------------------------------------- |
+| `permix/next` | Next.js App Router, resolver + dual RSC access |
+| `permix/tanstack-start` | TanStack Start middleware and SSR |
+| `permix/server` | Framework-agnostic fetch middleware |
+| `permix/svelte` | Svelte 5 runes |
+| `permix/drizzle` | Rules from Drizzle v1 schema |
+| `permix/drizzle/legacy` | Drizzle v0 (`>=0.30 <1`) |
+| `permix/effect` | Effect `Layer` / `Context` |
See the [examples directory](https://github.com/letstri/permix/tree/main/examples) (`next`, `tanstack-start`, `svelte`, `rebac`, and updated `react`, `vue`, …).
diff --git a/examples/next/app/actions.ts b/examples/next/app/actions.ts
index 45906646..a26e1793 100644
--- a/examples/next/app/actions.ts
+++ b/examples/next/app/actions.ts
@@ -2,8 +2,12 @@
import { revalidatePath } from 'next/cache'
import { cookies } from 'next/headers'
+import { createPermix } from 'permix'
import type { DemoRole } from '@/lib/auth'
+import { getSession } from '@/lib/auth'
+import type { PermissionsDefinition } from '@/lib/permissions'
+import { rulesForSession } from '@/lib/permissions'
export async function switchRole(formData: FormData) {
const role = formData.get('role')
@@ -21,3 +25,14 @@ export async function switchRole(formData: FormData) {
cookieStore.set('demo-role', role satisfies DemoRole, { path: '/' })
revalidatePath('/', 'layout')
}
+
+export async function createPost() {
+ const permix = createPermix()
+ permix.setup(rulesForSession(await getSession()))
+
+ if (!permix.check('post.create')) {
+ return { ok: false as const, error: 'Forbidden' }
+ }
+
+ return { ok: true as const, message: 'Post created (demo)' }
+}
diff --git a/examples/next/app/api/posts/route.ts b/examples/next/app/api/posts/route.ts
index f487a04a..a95da14c 100644
--- a/examples/next/app/api/posts/route.ts
+++ b/examples/next/app/api/posts/route.ts
@@ -1,6 +1,13 @@
-import { permix } from '@/lib/permix'
+import { createPermix } from 'permix'
+
+import { getSession } from '@/lib/auth'
+import type { PermissionsDefinition } from '@/lib/permissions'
+import { rulesForSession } from '@/lib/permissions'
export async function POST() {
+ const permix = createPermix()
+ permix.setup(rulesForSession(await getSession()))
+
if (!permix.check('post.create')) {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
diff --git a/examples/next/app/features/post-list.tsx b/examples/next/app/features/post-list.tsx
new file mode 100644
index 00000000..cb475d99
--- /dev/null
+++ b/examples/next/app/features/post-list.tsx
@@ -0,0 +1,75 @@
+import Link from 'next/link'
+import { Suspense } from 'react'
+
+import { permix } from '@/lib/permix'
+import type { Post } from '@/lib/permix'
+import { getPosts } from '@/lib/posts'
+
+import { CreatePostForm } from '../components/create-post-form'
+import { PermissionBadge } from '../components/permission-badge'
+import { PrivateEditIsland, PrivateEditIslandSkeleton } from './private-edit'
+
+export async function PostList() {
+ const [posts, canCreate] = await Promise.all([
+ getPosts(),
+ permix.check('post.create'),
+ ])
+
+ return (
+
+ Posts
+ {posts.map((post) => (
+
+
+
+
Post {post.id}
+
+ authorId: {post.authorId}
+
+
+
+
+
+ Open page
+
+
}>
+
+
+
+
+
+ ))}
+
+
+ )
+}
+
+async function PostUpdateBadge({ post }: { post: Post }) {
+ const allowed = await permix.check('post.update', post)
+ return
+}
+
+async function PostDeleteBadge({ post }: { post: Post }) {
+ const allowed = await permix.check('post.delete', post)
+ return
+}
+
+export function PostListSkeleton() {
+ return (
+
+ )
+}
diff --git a/examples/next/app/features/private-edit.tsx b/examples/next/app/features/private-edit.tsx
new file mode 100644
index 00000000..31c9b138
--- /dev/null
+++ b/examples/next/app/features/private-edit.tsx
@@ -0,0 +1,43 @@
+import { createPermix } from 'permix'
+
+import { getSession } from '@/lib/auth'
+import type { PermissionsDefinition } from '@/lib/permissions'
+import { rulesForSession } from '@/lib/permissions'
+import { permix } from '@/lib/permix'
+import { getPost } from '@/lib/posts'
+
+import { EditButton } from '../posts/[id]/edit-button'
+import { Providers } from '../providers'
+
+async function readPostUpdatePayload(postId: string) {
+ 'use cache: private'
+ const session = await getSession()
+ const instance = createPermix()
+ instance.setup(rulesForSession(session))
+ const post = await getPost(postId)
+ if (!post || !instance.check('post.update', post)) {
+ return null
+ }
+ return { id: post.id, authorId: post.authorId }
+}
+
+export async function PrivateEditIsland({ postId }: { postId: string }) {
+ const payload = await readPostUpdatePayload(postId)
+ if (!payload) {
+ return null
+ }
+
+ const [state, session] = await Promise.all([permix.dehydrate(), getSession()])
+
+ return (
+
+
+
+ )
+}
+
+export function PrivateEditIslandSkeleton() {
+ return (
+
+ )
+}
diff --git a/examples/next/app/features/public-read-badge.tsx b/examples/next/app/features/public-read-badge.tsx
new file mode 100644
index 00000000..511d6d99
--- /dev/null
+++ b/examples/next/app/features/public-read-badge.tsx
@@ -0,0 +1,12 @@
+import { publicPermix } from '@/lib/permix'
+
+import { PermissionBadge } from '../components/permission-badge'
+
+export async function PublicReadBadge() {
+ 'use cache'
+ const allowed = await publicPermix.check('post.read')
+
+ return (
+
+ )
+}
diff --git a/examples/next/app/features/server-checks.tsx b/examples/next/app/features/server-checks.tsx
new file mode 100644
index 00000000..b3ab9291
--- /dev/null
+++ b/examples/next/app/features/server-checks.tsx
@@ -0,0 +1,28 @@
+import { permix } from '@/lib/permix'
+import { getPosts } from '@/lib/posts'
+
+import { PermissionBadge } from '../components/permission-badge'
+import { SyncReadBadge } from './sync-read-badge'
+
+export async function ServerChecks() {
+ const posts = await getPosts()
+ const canCreate = await permix.check('post.create')
+ const canRead = await permix.check('post.read', posts[0])
+
+ return (
+
+ Server checks in this request
+
+
+ )
+}
+
+export function ServerChecksSkeleton() {
+ return (
+
+ )
+}
diff --git a/examples/next/app/features/session-panel.tsx b/examples/next/app/features/session-panel.tsx
new file mode 100644
index 00000000..307ebefb
--- /dev/null
+++ b/examples/next/app/features/session-panel.tsx
@@ -0,0 +1,27 @@
+import { getDemoRole, getSession } from '@/lib/auth'
+
+import { RoleSwitcher } from '../components/role-switcher'
+
+export async function SessionPanel() {
+ const [session, role] = await Promise.all([getSession(), getDemoRole()])
+
+ return (
+
+
+
+
Current session
+
+ {session ? session.label : 'Signed out (guest)'}
+
+
+
+
+
+ )
+}
+
+export function SessionPanelSkeleton() {
+ return (
+
+ )
+}
diff --git a/examples/next/app/features/sync-read-badge.tsx b/examples/next/app/features/sync-read-badge.tsx
new file mode 100644
index 00000000..5bf3e40e
--- /dev/null
+++ b/examples/next/app/features/sync-read-badge.tsx
@@ -0,0 +1,14 @@
+import { permix } from '@/lib/permix'
+
+import { PermissionBadge } from '../components/permission-badge'
+
+export function SyncReadBadge() {
+ const instance = permix.usePermix()
+
+ return (
+
+ )
+}
diff --git a/examples/next/app/layout.tsx b/examples/next/app/layout.tsx
index 9806f987..f7afe313 100644
--- a/examples/next/app/layout.tsx
+++ b/examples/next/app/layout.tsx
@@ -1,11 +1,6 @@
import type { Metadata } from 'next'
import { Geist, Geist_Mono } from 'next/font/google'
-import { getSession } from '@/lib/auth'
-import { adminTemplate, guestTemplate, permix } from '@/lib/permix'
-
-import { Providers } from './providers'
-
import './globals.css'
const geistSans = Geist({
@@ -23,39 +18,18 @@ export const metadata: Metadata = {
description: 'Live example of the permix/next integration',
}
-export default async function RootLayout({
+export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
- const session = await getSession()
-
- if (session) {
- permix.setup(
- session.role === 'admin'
- ? adminTemplate()
- : {
- post: {
- create: true,
- read: true,
- update: (post) => post?.authorId === session.userId,
- delete: false,
- },
- }
- )
- } else {
- permix.setup(guestTemplate())
- }
-
return (
-
- {children}
-
+ {children}
)
diff --git a/examples/next/app/page.tsx b/examples/next/app/page.tsx
index 2aef4e59..06b09096 100644
--- a/examples/next/app/page.tsx
+++ b/examples/next/app/page.tsx
@@ -1,29 +1,19 @@
-import Link from 'next/link'
+import { Suspense } from 'react'
-import { getDemoRole, getSession } from '@/lib/auth'
-import { permix } from '@/lib/permix'
-import { getPosts } from '@/lib/posts'
-
-import { CreatePostForm } from './components/create-post-form'
-import { PermissionBadge } from './components/permission-badge'
-import { RoleSwitcher } from './components/role-switcher'
-import { EditButton } from './posts/[id]/edit-button'
-
-export default async function Home() {
- const [session, role, posts] = await Promise.all([
- getSession(),
- getDemoRole(),
- getPosts(),
- ])
+import { PostList, PostListSkeleton } from './features/post-list'
+import { PublicReadBadge } from './features/public-read-badge'
+import { ServerChecks, ServerChecksSkeleton } from './features/server-checks'
+import { SessionPanel, SessionPanelSkeleton } from './features/session-panel'
+export default function Home() {
return (
-
+
Permix + Next.js App Router
- Per-request permissions demo
+ Request-safe permissions demo
This example mirrors the{' '}
@@ -33,76 +23,26 @@ export default async function Home() {
>
Next.js integration guide
- . Rules are set once in the root layout, checked on the server in
- pages and route handlers, then dehydrated for client components.
+ . A rules resolver initializes one cached instance per request. Static
+ chrome stays in the App Shell; session-aware checks stream behind
+ Suspense.
+
+
+
-
-
-
-
Current session
-
- {session ? session.label : 'Signed out (guest)'}
-
-
-
-
-
+ }>
+
+
-
- Server checks in this request
-
-
-
-
- Posts
- {posts.map((post) => (
-
-
-
-
Post {post.id}
-
- authorId: {post.authorId}
-
-
-
-
-
- Open page
-
-
-
-
-
- ))}
-
+ }>
+
+
-
+ }>
+
+
)
}
diff --git a/examples/next/app/posts/[id]/edit-button.tsx b/examples/next/app/posts/[id]/edit-button.tsx
index 63b0fb0a..79da33ab 100644
--- a/examples/next/app/posts/[id]/edit-button.tsx
+++ b/examples/next/app/posts/[id]/edit-button.tsx
@@ -1,12 +1,10 @@
'use client'
-import { usePermix } from 'permix/react'
-
-import { permix } from '@/app/providers'
-import type { Post } from '@/lib/permix'
+import { usePermix } from '@/lib/client-permix'
+import type { Post } from '@/lib/permissions'
export function EditButton({ post }: { post: Post }) {
- const { check } = usePermix(permix)
+ const { check } = usePermix()
if (!check('post.update', post)) {
return null
diff --git a/examples/next/app/posts/[id]/page.tsx b/examples/next/app/posts/[id]/page.tsx
index 5929aa93..07cdf701 100644
--- a/examples/next/app/posts/[id]/page.tsx
+++ b/examples/next/app/posts/[id]/page.tsx
@@ -1,23 +1,20 @@
import Link from 'next/link'
import { notFound } from 'next/navigation'
+import { Suspense } from 'react'
import { permix } from '@/lib/permix'
import { getPost } from '@/lib/posts'
-import { EditButton } from './edit-button'
+import {
+ PrivateEditIsland,
+ PrivateEditIslandSkeleton,
+} from '../../features/private-edit'
-export default async function PostPage({
+export default function PostPage({
params,
}: {
params: Promise<{ id: string }>
}) {
- const { id } = await params
- const post = await getPost(id)
-
- if (!post || !permix.check('post.read', post)) {
- notFound()
- }
-
return (
← Back to posts
-
-
-
- Post {post.id}
-
-
- authorId: {post.authorId}
-
-
- This page calls{' '}
-
- permix.check('post.read', post)
- {' '}
- on the server before rendering.
-
-
-
-
-
+ }>
+ {params.then(({ id }) => (
+
+ ))}
+
)
}
+
+async function PostArticle({ id }: { id: string }) {
+ const post = await getPost(id)
+
+ if (!post || !(await permix.check('post.read', post))) {
+ notFound()
+ }
+
+ return (
+
+ Post {post.id}
+
+ authorId: {post.authorId}
+
+
+ This page calls{' '}
+
+ await permix.check('post.read', post)
+ {' '}
+ on the server before rendering. The edit control is a privately cached
+ payload — a UI hint, not enforcement.
+
+
+
+ )
+}
+
+function PostArticleSkeleton() {
+ return (
+
+ )
+}
diff --git a/examples/next/app/providers.tsx b/examples/next/app/providers.tsx
index 636c72de..43edea75 100644
--- a/examples/next/app/providers.tsx
+++ b/examples/next/app/providers.tsx
@@ -1,14 +1,16 @@
'use client'
import type { DehydratedState } from 'permix'
-import { createPermix } from 'permix'
-import { PermixHydrate, PermixProvider } from 'permix/react'
import { useLayoutEffect } from 'react'
import type { Session } from '@/lib/auth'
-import type { PermissionsDefinition } from '@/lib/permix'
-
-const permix = createPermix()
+import {
+ clientPermix,
+ PermixHydrate,
+ PermixProvider,
+} from '@/lib/client-permix'
+import { rulesForSession } from '@/lib/permissions'
+import type { PermissionsDefinition } from '@/lib/permissions'
function ClientRulesSetup({
session,
@@ -18,14 +20,7 @@ function ClientRulesSetup({
children: React.ReactNode
}) {
useLayoutEffect(() => {
- permix.setup({
- post: {
- create: !!session,
- read: true,
- update: (post) => post?.authorId === session?.userId,
- delete: session?.role === 'admin',
- },
- })
+ clientPermix.setup(rulesForSession(session))
}, [session])
return children
@@ -41,12 +36,10 @@ export function Providers({
children: React.ReactNode
}) {
return (
-
+
{children}
)
}
-
-export { permix }
diff --git a/examples/next/lib/client-permix.ts b/examples/next/lib/client-permix.ts
new file mode 100644
index 00000000..64a67907
--- /dev/null
+++ b/examples/next/lib/client-permix.ts
@@ -0,0 +1,11 @@
+import { createPermix } from 'permix/react'
+
+import type { PermissionsDefinition } from './permissions'
+
+export const {
+ permix: clientPermix,
+ PermixProvider,
+ PermixHydrate,
+ usePermix,
+ Check,
+} = createPermix()
diff --git a/examples/next/lib/permissions.ts b/examples/next/lib/permissions.ts
new file mode 100644
index 00000000..9b89d945
--- /dev/null
+++ b/examples/next/lib/permissions.ts
@@ -0,0 +1,51 @@
+import { createTemplate } from 'permix'
+import type { Rules, ValidateDefinition } from 'permix'
+
+import type { Session } from './auth'
+
+export interface Post {
+ id: string
+ authorId: string
+}
+
+export type PermissionsDefinition = ValidateDefinition<{
+ post: [
+ { name: 'create'; type: Post },
+ { name: 'read'; type: Post },
+ { name: 'update'; type: Post },
+ { name: 'delete'; type: Post },
+ ]
+}>
+
+export const adminTemplate = createTemplate({
+ post: { create: true, read: true, update: true, delete: true },
+})
+
+export const guestTemplate = createTemplate({
+ post: { create: false, read: true, update: false, delete: false },
+})
+
+export const publicReadTemplate = createTemplate({
+ post: { create: false, read: true, update: false, delete: false },
+})
+
+export function rulesForSession(
+ session: Session | null
+): Rules {
+ if (!session) {
+ return guestTemplate()
+ }
+
+ if (session.role === 'admin') {
+ return adminTemplate()
+ }
+
+ return {
+ post: {
+ create: true,
+ read: true,
+ update: (post) => post?.authorId === session.userId,
+ delete: false,
+ },
+ }
+}
diff --git a/examples/next/lib/permix.ts b/examples/next/lib/permix.ts
index c3cf0b92..dfb46986 100644
--- a/examples/next/lib/permix.ts
+++ b/examples/next/lib/permix.ts
@@ -1,26 +1,16 @@
-import type { ValidateDefinition } from 'permix'
import { createPermix } from 'permix/next'
-export interface Post {
- id: string
- authorId: string
-}
+import { getSession } from './auth'
+import type { PermissionsDefinition } from './permissions'
+import { publicReadTemplate, rulesForSession } from './permissions'
-export type PermissionsDefinition = ValidateDefinition<{
- post: [
- { name: 'create'; type: Post },
- { name: 'read'; type: Post },
- { name: 'update'; type: Post },
- { name: 'delete'; type: Post },
- ]
-}>
+export const permix = createPermix(async () =>
+ rulesForSession(await getSession())
+)
-export const permix = createPermix()
+export const publicPermix = createPermix(() =>
+ publicReadTemplate()
+)
-export const adminTemplate = permix.template({
- post: { create: true, read: true, update: true, delete: true },
-})
-
-export const guestTemplate = permix.template({
- post: { create: false, read: true, update: false, delete: false },
-})
+export { adminTemplate, guestTemplate, rulesForSession } from './permissions'
+export type { PermissionsDefinition, Post } from './permissions'
diff --git a/examples/next/next.config.ts b/examples/next/next.config.ts
index 2f186bb4..53d3bac5 100644
--- a/examples/next/next.config.ts
+++ b/examples/next/next.config.ts
@@ -2,6 +2,14 @@ import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
transpilePackages: ['permix'],
+ cacheComponents: true,
+ partialPrefetching: true,
+ experimental:
+ process.env.EXPOSE_TESTING_API === '1'
+ ? {
+ exposeTestingApiInProductionBuild: true,
+ }
+ : undefined,
}
export default nextConfig
diff --git a/examples/next/package.json b/examples/next/package.json
index b11fd741..ce9c8d82 100644
--- a/examples/next/package.json
+++ b/examples/next/package.json
@@ -8,7 +8,7 @@
"start": "next start"
},
"dependencies": {
- "next": "16.2.6",
+ "next": "16.3.3",
"permix": "workspace:*",
"react": "catalog:",
"react-dom": "catalog:"
diff --git a/ignores.ts b/ignores.ts
index 8edf9972..7c1a1fa8 100644
--- a/ignores.ts
+++ b/ignores.ts
@@ -15,4 +15,8 @@ export const ignorePatterns = [
'**/*.generated.ts',
'examples/svelte/src/lib/index.ts',
'pnpm-lock.yaml',
+ 'permix/test/next/.scratch/**',
+ 'permix/test/next/playwright-report/**',
+ 'permix/test/next/test-results/**',
+ 'permix/test/next/**',
]
diff --git a/package.json b/package.json
index d07f6b74..f17ded64 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"prepare": "husky",
"publish": "cd permix && pnpm publish",
"test": "turbo run test --filter=permix",
+ "test:next": "pnpm --filter permix build && pnpm --filter @permix/next-integration test",
"update": "taze -r -I major"
},
"devDependencies": {
diff --git a/permix/package.json b/permix/package.json
index 79f661aa..8ffa5b1a 100644
--- a/permix/package.json
+++ b/permix/package.json
@@ -176,7 +176,7 @@
"fastify": ">=5",
"fastify-plugin": ">=5",
"hono": ">=4",
- "next": ">=14",
+ "next": ">=15",
"react": ">=18",
"react-dom": ">=18",
"solid-js": ">=1",
diff --git a/permix/src/next/hydration.test.tsx b/permix/src/next/hydration.test.tsx
index ddb0e13c..6fdc7c85 100644
--- a/permix/src/next/hydration.test.tsx
+++ b/permix/src/next/hydration.test.tsx
@@ -1,47 +1,38 @@
import { render } from '@testing-library/react'
-import { describe, expect, it, vi } from 'vitest'
+import { afterEach, describe, expect, it, vi } from 'vitest'
import { createPermix as createCorePermix } from '../core'
import { PermixHydrate, PermixProvider, usePermix } from '../react'
import { createPermix as createNextPermix } from './permix'
+import { resetRequestCache } from './request-cache-mock'
import '@testing-library/jest-dom/vitest'
-// See ./permix.test.ts — outside a Next.js request scope `cache()` does not
-// memoize, so we mock it for the test environment.
vi.mock('react', async () => {
const actual = await vi.importActual('react')
+ const { createRequestScopedCache } = await import('./request-cache-mock')
return {
...actual,
- cache: any>(fn: T): T => {
- const store = new Map>()
- return ((...args: Parameters) => {
- const key = JSON.stringify(args)
- if (!store.has(key)) {
- store.set(key, fn(...args))
- }
- return store.get(key)!
- }) as T
- },
+ cache: createRequestScopedCache,
}
})
describe('next → react hydration round-trip', () => {
+ afterEach(() => {
+ resetRequestCache()
+ })
+
it('hydrates dehydrated server state on the client', async () => {
- // Server: setup + dehydrate using the Next.js per-request helper.
const permixServer = createNextPermix<{
post: ['create', 'read']
- }>()
-
- permixServer.setup({
+ }>(() => ({
post: {
create: true,
read: false,
},
- })
+ }))
- const state = permixServer.dehydrate()
+ const state = await permixServer.dehydrate()
- // Client: separate singleton + hydrate via the react integration.
const permixClient = createCorePermix<{
post: ['create', 'read']
}>()
diff --git a/permix/src/next/permix.test.ts b/permix/src/next/permix.test.ts
index 1b591908..34d9633e 100644
--- a/permix/src/next/permix.test.ts
+++ b/permix/src/next/permix.test.ts
@@ -1,93 +1,94 @@
-import { describe, expect, it, vi } from 'vitest'
+import { afterEach, describe, expect, it, vi } from 'vitest'
import { createPermix } from './permix'
+import { resetRequestCache } from './request-cache-mock'
-// React's `cache()` only memoizes within a Next.js request scope (backed by
-// AsyncLocalStorage). Outside of one — including in vitest — it returns a
-// fresh value on every call, which would defeat the per-request instance
-// pattern. We replace it here with a simple module-level memoizer so that a
-// single `createPermix()` call behaves as if it were running inside one
-// Next.js request for the duration of the test.
vi.mock('react', async () => {
const actual = await vi.importActual('react')
+ const { createRequestScopedCache } = await import('./request-cache-mock')
return {
...actual,
- cache: any>(fn: T): T => {
- const store = new Map>()
- return ((...args: Parameters) => {
- const key = JSON.stringify(args)
- if (!store.has(key)) {
- store.set(key, fn(...args))
+ cache: createRequestScopedCache,
+ use: (usable: T | PromiseLike): T => {
+ if (
+ usable !== null &&
+ usable !== undefined &&
+ typeof (usable as PromiseLike).then === 'function'
+ ) {
+ const thenable = usable as PromiseLike & {
+ status?: 'fulfilled' | 'rejected' | 'pending'
+ value?: T
+ reason?: unknown
}
- return store.get(key)!
- }) as T
+ if (thenable.status === 'fulfilled') {
+ return thenable.value as T
+ }
+ if (thenable.status === 'rejected') {
+ throw thenable.reason
+ }
+ throw thenable
+ }
+ return usable as T
},
}
})
describe('next createPermix', () => {
- it('sets up rules and checks permissions', () => {
+ afterEach(() => {
+ resetRequestCache()
+ })
+
+ it('initializes from a sync resolver and checks permissions', async () => {
const permix = createPermix<{
post: ['create', 'read']
- }>()
-
- permix.setup({
+ }>(() => ({
post: {
create: true,
read: false,
},
- })
+ }))
- expect(permix.check('post.create')).toBe(true)
- expect(permix.check('post.read')).toBe(false)
+ await expect(permix.check('post.create')).resolves.toBe(true)
+ await expect(permix.check('post.read')).resolves.toBe(false)
})
- it('works with data resolved before setup', async () => {
+ it('initializes from an async resolver', async () => {
const permix = createPermix<{
post: ['create']
- }>()
-
- // Mimic the documented pattern: await your own data, then call setup
- // with plain rules. No async wrapper around setup needed.
- const user = await Promise.resolve({ role: 'admin' as const })
-
- permix.setup({
- post: {
- create: user.role === 'admin',
- },
+ }>(async () => {
+ const user = await Promise.resolve({ role: 'admin' as const })
+ return {
+ post: {
+ create: user.role === 'admin',
+ },
+ }
})
- expect(permix.check('post.create')).toBe(true)
+ await expect(permix.check('post.create')).resolves.toBe(true)
})
- it('exposes the underlying core instance via get()', () => {
+ it('returns the initialized core instance from getPermix', async () => {
const permix = createPermix<{
post: ['create']
- }>()
+ }>(() => ({ post: { create: true } }))
- permix.setup({ post: { create: true } })
-
- const core = permix.get()
+ const core = await permix.getPermix()
expect(core.isReady()).toBe(true)
expect(core.check('post.create')).toBe(true)
})
- it('reads the current rules with getRules', () => {
+ it('reads the current rules with getRules', async () => {
const permix = createPermix<{
post: ['create', 'read']
- }>()
-
- expect(permix.getRules()).toBeNull()
-
- permix.setup({
+ }>(() => ({
post: {
create: true,
read: false,
},
- })
+ }))
- expect(permix.getRules()).toStrictEqual({
+ await expect(permix.getRules()).resolves.toStrictEqual({
post: {
create: true,
read: false,
@@ -95,19 +96,17 @@ describe('next createPermix', () => {
})
})
- it('dehydrates the request-scoped state', () => {
+ it('dehydrates the request-scoped state', async () => {
const permix = createPermix<{
post: ['create', 'read']
- }>()
-
- permix.setup({
+ }>(() => ({
post: {
create: true,
read: false,
},
- })
+ }))
- expect(permix.dehydrate()).toStrictEqual({
+ await expect(permix.dehydrate()).resolves.toStrictEqual({
post: {
create: true,
read: false,
@@ -115,35 +114,86 @@ describe('next createPermix', () => {
})
})
- it('reuses the same instance across calls in the same request scope', () => {
+ it('shares one initialized instance across concurrent callers', async () => {
+ let calls = 0
const permix = createPermix<{
post: ['create']
- }>()
+ }>(async () => {
+ calls++
+ await Promise.resolve()
+ return { post: { create: true } }
+ })
- permix.setup({ post: { create: true } })
+ const [first, second, allowed] = await Promise.all([
+ permix.getPermix(),
+ permix.getPermix(),
+ permix.check('post.create'),
+ ])
- // Two get() calls in the same "request" must return the same instance,
- // proving that setup() persists across subsequent calls.
- expect(permix.get()).toBe(permix.get())
- expect(permix.check('post.create')).toBe(true)
+ expect(calls).toBe(1)
+ expect(first).toBe(second)
+ expect(allowed).toBe(true)
+ expect(first.check('post.create')).toBe(true)
})
- it('isolates state between independent factories', () => {
- const permixA = createPermix<{ post: ['create'] }>()
- const permixB = createPermix<{ post: ['create'] }>()
+ it('isolates state between independent factories', async () => {
+ const permixA = createPermix<{ post: ['create'] }>(() => ({
+ post: { create: true },
+ }))
+ const permixB = createPermix<{ post: ['create'] }>(() => ({
+ post: { create: false },
+ }))
+
+ await expect(permixA.check('post.create')).resolves.toBe(true)
+ await expect(permixB.check('post.create')).resolves.toBe(false)
+ const [coreA, coreB] = await Promise.all([
+ permixA.getPermix(),
+ permixB.getPermix(),
+ ])
+ expect(coreA).not.toBe(coreB)
+ })
+
+ it('isolates state across simulated requests of the same factory', async () => {
+ let requestRole: 'admin' | 'guest' = 'admin'
+ const permix = createPermix<{ post: ['create'] }>(() => ({
+ post: { create: requestRole === 'admin' },
+ }))
+
+ await expect(permix.check('post.create')).resolves.toBe(true)
+ const first = await permix.getPermix()
+
+ resetRequestCache()
+ requestRole = 'guest'
+
+ await expect(permix.check('post.create')).resolves.toBe(false)
+ await expect(permix.getPermix()).resolves.not.toBe(first)
+ })
+
+ it('usePermix unwraps the same initialized instance', async () => {
+ const permix = createPermix<{
+ post: ['create']
+ }>(() => ({ post: { create: true } }))
- permixA.setup({ post: { create: true } })
- permixB.setup({ post: { create: false } })
+ const instancePromise = permix.getPermix()
+ const instance = await instancePromise
+ Object.assign(instancePromise, {
+ status: 'fulfilled',
+ value: instance,
+ })
- expect(permixA.check('post.create')).toBe(true)
- expect(permixB.check('post.create')).toBe(false)
- expect(permixA.get()).not.toBe(permixB.get())
+ expect(permix.usePermix()).toBe(instance)
+ expect(instance.check('post.create')).toBe(true)
})
- it('creates reusable templates', () => {
+ it('creates reusable templates without waiting on initialization', () => {
const permix = createPermix<{
post: ['create', 'read']
- }>()
+ }>(() => ({
+ post: {
+ create: false,
+ read: false,
+ },
+ }))
const adminTemplate = permix.template({
post: {
@@ -163,7 +213,11 @@ describe('next createPermix', () => {
it('supports parameterized templates', () => {
const permix = createPermix<{
post: [{ name: 'edit'; type: { authorId: string } }]
- }>()
+ }>(() => ({
+ post: {
+ edit: () => false,
+ },
+ }))
const template = permix.template((userId: string) => ({
post: {
diff --git a/permix/src/next/permix.ts b/permix/src/next/permix.ts
index 742b3211..96b19b39 100644
--- a/permix/src/next/permix.ts
+++ b/permix/src/next/permix.ts
@@ -1,101 +1,107 @@
-import { cache } from 'react'
+import { cache, use } from 'react'
import type { Permix as PermixCore } from '../core'
import { createPermix as createPermixCore, createTemplate } from '../core'
+import type { CheckArgs } from '../core/check'
import type { Definition } from '../core/definitions'
-import type { PermixHooks, Rules, RulesPaths } from '../core/permix'
+import type { Rules, RulesPaths } from '../core/permix'
import type { DehydratedState } from '../core/rules'
+export type ResolveRules = () =>
+ | Rules
+ | Promise>
+
/**
- * Create a per-request Permix instance for Next.js App Router.
+ * Create a per-request Permix helper for the Next.js App Router.
+ *
+ * Pass a sync or async rules resolver. React's `cache()` memoizes one Promise
+ * of a fully initialized core instance per request, so concurrent RSC callers
+ * share the same setup instead of racing layout mutation order.
+ *
+ * Use the async getters from async Server Components, Route Handlers, and
+ * Server Actions. Use `usePermix()` from non-async Server Components — it
+ * unwraps the same Promise with React `use()`, so `check()` stays synchronous
+ * at the call site while React may suspend only while rules resolve.
*
- * Backed by React's `cache()`, so all server components, route handlers, and
- * server actions within the same request share one instance while concurrent
- * requests stay fully isolated.
+ * Route Handlers and Server Actions do not share the RSC `cache()` identity.
+ * Prefer an explicit core `createPermix()` + `setup()` inside each invocation
+ * when those entry points must authorize independently of the render tree.
*
* @example
* ```ts
* // lib/permix.ts
* import { createPermix } from 'permix/next'
+ * import { getSession } from '@/lib/auth'
*
* export const permix = createPermix<{
* post: ['create', 'read', 'update', 'delete']
- * }>()
- * ```
- *
- * ```tsx
- * // app/layout.tsx (server component)
- * import { permix } from '@/lib/permix'
- * import { getSession } from '@/lib/auth'
- *
- * export default async function RootLayout({ children }) {
+ * }>(async () => {
* const session = await getSession()
- *
- * permix.setup({
+ * return {
* post: {
* create: !!session,
* read: true,
* update: session?.role === 'admin',
* delete: session?.role === 'admin',
* },
- * })
+ * }
+ * })
+ * ```
+ *
+ * ```tsx
+ * // async Server Component
+ * const canCreate = await permix.check('post.create')
*
- * return {children}
- * }
+ * // non-async Server Component
+ * const instance = permix.usePermix()
+ * const canRead = instance.check('post.read')
* ```
*
* @link https://permix.letstri.dev/docs/integrations/next
*/
-export function createPermix() {
- // Per-request isolation: concurrent requests each get their own instance;
- // multiple callers within one request share the same one.
- const getPermix = cache((): PermixCore => createPermixCore())
+export function createPermix(
+ resolveRules: ResolveRules
+) {
+ const getInitializedPermix = cache(async (): Promise> => {
+ const permix = createPermixCore()
+ permix.setup(await Promise.resolve(resolveRules()))
+ return permix
+ })
- function setup(rules: Rules): void {
- getPermix().setup(rules)
+ function getPermix(): Promise> {
+ return getInitializedPermix()
}
- const check: PermixCore['check'] = (...args) => getPermix().check(...args)
+ function usePermix(): PermixCore {
+ return use(getInitializedPermix())
+ }
- function dehydrate(): DehydratedState {
- return getPermix().dehydrate()
+ async function check(...args: CheckArgs): Promise {
+ const permix = await getInitializedPermix()
+ return permix.check(...args)
}
- function get(): PermixCore {
- return getPermix()
+ async function getRules(): Promise | null> {
+ const permix = await getInitializedPermix()
+ return permix.getRules()
}
- function getRules(): Rules | null {
- return getPermix().getRules()
+ async function dehydrate(): Promise> {
+ const permix = await getInitializedPermix()
+ return permix.dehydrate()
}
function template(rules: Rules | ((param: T) => Rules)) {
return createTemplate(rules)
}
- function hook>(
- name: K,
- fn: PermixHooks[K]
- ) {
- return getPermix().hook(name, fn)
- }
-
- function hookOnce>(
- name: K,
- fn: PermixHooks[K]
- ) {
- getPermix().hookOnce(name, fn)
- }
-
return {
- setup,
+ getPermix,
+ usePermix,
check,
- dehydrate,
- get,
getRules,
+ dehydrate,
template,
- hook,
- hookOnce,
$inferDefinition: undefined as unknown as D,
$inferPath: undefined as unknown as RulesPaths,
}
diff --git a/permix/src/next/request-cache-mock.ts b/permix/src/next/request-cache-mock.ts
new file mode 100644
index 00000000..b06f4045
--- /dev/null
+++ b/permix/src/next/request-cache-mock.ts
@@ -0,0 +1,34 @@
+/**
+ * Request-scoped stand-in for React's `cache()` in vitest.
+ *
+ * Real `cache()` memoizes within a Next.js request (AsyncLocalStorage). In
+ * vitest there is no request scope, so the default implementation does not
+ * share work across callers. This helper memoizes like one request, and
+ * {@link resetRequestCache} starts a new generation so tests can simulate
+ * the next request without pretending the cache is process-global forever.
+ */
+let generation = 0
+
+export function resetRequestCache(): void {
+ generation++
+}
+
+export function createRequestScopedCache any>(
+ fn: T
+): T {
+ const stores = new Map>>()
+
+ return ((...args: Parameters) => {
+ let store = stores.get(generation)
+ if (!store) {
+ store = new Map()
+ stores.set(generation, store)
+ }
+
+ const key = JSON.stringify(args)
+ if (!store.has(key)) {
+ store.set(key, fn(...args) as ReturnType)
+ }
+ return store.get(key)!
+ }) as T
+}
diff --git a/permix/test-d/public-api.ts b/permix/test-d/public-api.ts
index 19108ac1..bb5b99f3 100644
--- a/permix/test-d/public-api.ts
+++ b/permix/test-d/public-api.ts
@@ -43,7 +43,12 @@ const svelte = createSvelteComponents(core)
const drizzle = createDrizzlePermix({})
const drizzleLegacy = createDrizzleLegacyPermix({})
const effect = createEffectPermix()
-const next = createNextPermix()
+const next = createNextPermix(() => ({
+ post: {
+ create: true,
+ read: true,
+ },
+}))
const tanstackStart = createTanstackStartPermix()
core.setup({
@@ -82,3 +87,4 @@ export {
trpc,
vue
}
+
diff --git a/permix/test/next/.gitignore b/permix/test/next/.gitignore
new file mode 100644
index 00000000..90d593e7
--- /dev/null
+++ b/permix/test/next/.gitignore
@@ -0,0 +1,4 @@
+.scratch
+test-results
+playwright-report
+blob-report
diff --git a/permix/test/next/instant-nav.rig.md b/permix/test/next/instant-nav.rig.md
new file mode 100644
index 00000000..f2987b10
--- /dev/null
+++ b/permix/test/next/instant-nav.rig.md
@@ -0,0 +1,10 @@
+# instant-nav rig: permix Next.js adapter fixtures
+
+- BUILD: `EXPOSE_TESTING_API=1` plus the versioned `next` binary from this package (`next-15`, `next-16-0`, `next-16-3`) run as `next build && next start` inside `permix/test/next/.scratch/`. Never `next dev`.
+- EXPOSE: `EXPOSE_TESTING_API=1` at **build** time, wired to `experimental.exposeTestingApiInProductionBuild` in the 16.3 overlay config. Compat versions (15.5.24, 16.0.11) do not run `instant()`.
+- RUN: `pnpm --filter @permix/next-integration test` (or `node permix/test/next/run.mjs` after `pnpm --filter permix build`). Playwright uses `BASE_URL` from the launcher. Desktop 1280×720 and mobile 390×844 projects run the 16.3 instant-navigation file.
+- TEST USER: cookie `demo-user=alice` (can create/update) or `demo-user=bob` (read only). No login helper; tests call `context.addCookies`. Public/cache-safe checks do not depend on the cookie.
+- DRIFT: cookie value, tenant root param (`acme` vs `globex`), whether a `"use cache: private"` payload was primed, Partial Prefetching vs `prefetch={true}`. The cold session island uses `connection()` so the testing lock can gate it; cookie reads alone are not a lock probe.
+- LOOP: local `build → start → playwright` via `run.mjs`; CI job `.github/workflows/next-integration.yml` does the same. Fully agent-drivable; no secrets.
+- LIVENESS: n/a — each run builds and starts a local server on a free port, then stops that process group.
+- WALLS: `permix` must be built (`pnpm --filter permix build`) so the fixture can import `permix`/`permix/next`. Playwright Chromium is installed with `pnpm exec playwright install chromium`. Next 15/16.0 configs must not set `cacheComponents` or `partialPrefetching`.
diff --git a/permix/test/next/package.json b/permix/test/next/package.json
new file mode 100644
index 00000000..671bf310
--- /dev/null
+++ b/permix/test/next/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@permix/next-integration",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "test": "node ./run.mjs"
+ },
+ "dependencies": {
+ "permix": "workspace:*",
+ "react": "catalog:",
+ "react-dom": "catalog:"
+ },
+ "devDependencies": {
+ "@next/playwright": "16.3.3",
+ "@playwright/test": "^1.55.1",
+ "@types/node": "catalog:",
+ "@types/react": "catalog:",
+ "@types/react-dom": "catalog:",
+ "next-15": "npm:next@15.5.24",
+ "next-16-0": "npm:next@16.0.11",
+ "next-16-3": "npm:next@16.3.3",
+ "typescript": "catalog:",
+ "typescript59": "catalog:typescript-classic"
+ }
+}
diff --git a/permix/test/next/playwright.config.ts b/permix/test/next/playwright.config.ts
new file mode 100644
index 00000000..17224ef9
--- /dev/null
+++ b/permix/test/next/playwright.config.ts
@@ -0,0 +1,36 @@
+import { defineConfig } from '@playwright/test'
+
+const baseURL = process.env.BASE_URL ?? 'http://127.0.0.1:3000'
+const isPpr = process.env.PERMIX_NEXT_VERSION === '16.3.3'
+
+export default defineConfig({
+ testDir: './tests',
+ fullyParallel: false,
+ forbidOnly: !!process.env.CI,
+ retries: 0,
+ use: {
+ baseURL,
+ trace: 'off',
+ },
+ projects: [
+ {
+ name: 'compat',
+ testMatch: /compat\.spec\.ts/,
+ use: { viewport: { width: 1280, height: 720 } },
+ },
+ ...(isPpr
+ ? [
+ {
+ name: 'desktop',
+ testMatch: /instant\.spec\.ts/,
+ use: { viewport: { width: 1280, height: 720 } },
+ },
+ {
+ name: 'mobile',
+ testMatch: /instant\.spec\.ts/,
+ use: { viewport: { width: 390, height: 844 } },
+ },
+ ]
+ : []),
+ ],
+})
diff --git a/permix/test/next/run.mjs b/permix/test/next/run.mjs
new file mode 100644
index 00000000..828302da
--- /dev/null
+++ b/permix/test/next/run.mjs
@@ -0,0 +1,183 @@
+import { spawn } from 'node:child_process'
+import { cp, mkdir, rm, symlink } from 'node:fs/promises'
+import { createRequire } from 'node:module'
+import { createServer } from 'node:net'
+import path from 'node:path'
+
+const here = import.meta.dirname
+const require = createRequire(import.meta.url)
+const permixRoot = path.resolve(here, '../..')
+
+const versions = [
+ { id: '15.5.24', alias: 'next-15', ppr: false, typescript: 'typescript59' },
+ { id: '16.0.11', alias: 'next-16-0', ppr: false, typescript: 'typescript59' },
+ { id: '16.3.3', alias: 'next-16-3', ppr: true, typescript: 'typescript' },
+]
+
+function packageDir(specifier) {
+ return path.dirname(require.resolve(`${specifier}/package.json`))
+}
+
+function getFreePort() {
+ return new Promise((resolve, reject) => {
+ const server = createServer()
+ server.listen(0, '127.0.0.1', () => {
+ const address = server.address()
+ if (!address || typeof address === 'string') {
+ server.close()
+ reject(new Error('Failed to allocate a port'))
+ return
+ }
+ const port = address.port
+ server.close((error) => {
+ if (error) {
+ reject(error)
+ return
+ }
+ resolve(port)
+ })
+ })
+ server.on('error', reject)
+ })
+}
+
+function run(command, args, options) {
+ return new Promise((resolve, reject) => {
+ const child = spawn(command, args, {
+ stdio: 'inherit',
+ ...options,
+ })
+ child.on('error', reject)
+ child.on('exit', (code, signal) => {
+ if (code === 0) {
+ resolve()
+ return
+ }
+ reject(
+ new Error(`${command} ${args.join(' ')} failed (${code ?? signal})`)
+ )
+ })
+ })
+}
+
+async function waitForServer(url, timeoutMs = 60_000) {
+ const start = Date.now()
+ while (Date.now() - start < timeoutMs) {
+ try {
+ const response = await fetch(url, { redirect: 'manual' })
+ await response.arrayBuffer()
+ return
+ } catch {
+ await new Promise((resolve) => setTimeout(resolve, 250))
+ }
+ }
+ throw new Error(`Timed out waiting for ${url}`)
+}
+
+async function prepare(version) {
+ const dest = path.join(here, '.scratch', version.id)
+ await rm(dest, { recursive: true, force: true })
+ await cp(path.join(here, 'src'), dest, { recursive: true })
+ if (version.ppr) {
+ await cp(path.join(here, 'src-ppr'), dest, { recursive: true })
+ await rm(path.join(dest, 'app/layout.tsx'), { force: true })
+ await rm(path.join(dest, 'app/page.tsx'), { force: true })
+ }
+
+ const modules = path.join(dest, 'node_modules')
+ await mkdir(modules, { recursive: true })
+ await symlink(packageDir(version.alias), path.join(modules, 'next'))
+ await symlink(packageDir('react'), path.join(modules, 'react'))
+ await symlink(packageDir('react-dom'), path.join(modules, 'react-dom'))
+ await symlink(packageDir(version.typescript), path.join(modules, 'typescript'))
+ await symlink(permixRoot, path.join(modules, 'permix'))
+ return dest
+}
+
+async function nextBin(alias) {
+ const nextPackage = packageDir(alias)
+ return path.join(nextPackage, 'dist/bin/next')
+}
+
+async function withServer(version, dest, fn) {
+ const port = await getFreePort()
+ const baseURL = `http://127.0.0.1:${port}`
+ const bin = await nextBin(version.alias)
+ const child = spawn(process.execPath, [bin, 'start', '-p', String(port)], {
+ cwd: dest,
+ env: { ...process.env, PORT: String(port) },
+ stdio: 'inherit',
+ detached: process.platform !== 'win32',
+ })
+
+ const stop = async () => {
+ if (child.pid && process.platform !== 'win32') {
+ try {
+ process.kill(-child.pid, 'SIGTERM')
+ } catch {
+ child.kill('SIGTERM')
+ }
+ } else {
+ child.kill('SIGTERM')
+ }
+ }
+
+ try {
+ await waitForServer(baseURL)
+ await fn(baseURL)
+ } finally {
+ await stop()
+ }
+}
+
+async function main() {
+ const dist = path.join(permixRoot, 'dist/core/index.mjs')
+ try {
+ await import(path.join(permixRoot, 'dist/next/index.mjs'))
+ } catch {
+ throw new Error(
+ `permix must be built before running Next fixtures (missing ${dist}). Run pnpm --filter permix build.`
+ )
+ }
+
+ const only = process.env.PERMIX_NEXT_VERSION
+ const selected = only
+ ? versions.filter((version) => version.id === only)
+ : versions
+ if (only && selected.length === 0) {
+ throw new Error(`Unknown PERMIX_NEXT_VERSION: ${only}`)
+ }
+
+ for (const version of selected) {
+ console.log(`\n=== Next ${version.id} ===`)
+ const dest = await prepare(version)
+ const bin = await nextBin(version.alias)
+ await run(process.execPath, [bin, 'build'], {
+ cwd: dest,
+ env: {
+ ...process.env,
+ EXPOSE_TESTING_API: '1',
+ PERMIX_NEXT_VERSION: version.id,
+ },
+ })
+ await withServer(version, dest, async (baseURL) => {
+ await run(
+ 'pnpm',
+ ['exec', 'playwright', 'test', '--config', 'playwright.config.ts'],
+ {
+ cwd: here,
+ env: {
+ ...process.env,
+ BASE_URL: baseURL,
+ PERMIX_NEXT_VERSION: version.id,
+ },
+ }
+ )
+ })
+ }
+}
+
+main().catch((error) => {
+ console.error(error)
+ process.exit(1)
+})
diff --git a/permix/test/next/src-ppr/app/[tenant]/dashboard/page.tsx b/permix/test/next/src-ppr/app/[tenant]/dashboard/page.tsx
new file mode 100644
index 00000000..9809fe2b
--- /dev/null
+++ b/permix/test/next/src-ppr/app/[tenant]/dashboard/page.tsx
@@ -0,0 +1,10 @@
+import { PermissionHoles } from '../../features/permission-holes'
+
+export default function DashboardPage() {
+ return (
+
+ Dashboard
+
+
+ )
+}
diff --git a/permix/test/next/src-ppr/app/[tenant]/layout.tsx b/permix/test/next/src-ppr/app/[tenant]/layout.tsx
new file mode 100644
index 00000000..d3516983
--- /dev/null
+++ b/permix/test/next/src-ppr/app/[tenant]/layout.tsx
@@ -0,0 +1,59 @@
+import Link from 'next/link'
+import type { ReactNode } from 'react'
+import { Suspense } from 'react'
+
+import { CacheSafeCheck } from '../features/cache-safe-check'
+
+export function generateStaticParams() {
+ return [{ tenant: 'acme' }, { tenant: 'globex' }]
+}
+
+export default function TenantLayout({
+ children,
+ params,
+}: {
+ children: ReactNode
+ params: Promise<{ tenant: string }>
+}) {
+ return (
+
+
+
+
+
+ {params.then(({ tenant }) => (
+ {tenant}
+ ))}
+
+ Acme
+
+
+ Globex
+
+
+ Dashboard
+
+
+ Dashboard (prefetch)
+
+
+ Globex (prefetch)
+
+
+ loading cache-safe
+ }
+ >
+
+
+ {children}
+
+
+
+ )
+}
diff --git a/permix/test/next/src-ppr/app/[tenant]/page.tsx b/permix/test/next/src-ppr/app/[tenant]/page.tsx
new file mode 100644
index 00000000..90a68246
--- /dev/null
+++ b/permix/test/next/src-ppr/app/[tenant]/page.tsx
@@ -0,0 +1,44 @@
+import { Suspense } from 'react'
+
+import { ActionCheck } from '../features/action-check'
+import { ConcurrentInstances } from '../features/concurrent-instances'
+import { PermissionHoles } from '../features/permission-holes'
+import { PublicRead } from '../features/public-read'
+import { SessionCreate } from '../features/session-create'
+import { UsePermixCreate } from '../features/use-permix-create'
+
+export default function TenantHome({
+ params,
+}: {
+ params: Promise<{ tenant: string }>
+}) {
+ return (
+
+ {params.then(({ tenant }) => (
+
+ Tenant home
+
+ ))}
+ loading public
}>
+
+
+ loading instances}
+ >
+
+
+ loading session}
+ >
+
+
+ loading usePermix}
+ >
+
+
+
+
+
+ )
+}
diff --git a/permix/test/next/src-ppr/app/features/cache-safe-check.tsx b/permix/test/next/src-ppr/app/features/cache-safe-check.tsx
new file mode 100644
index 00000000..ad4765ee
--- /dev/null
+++ b/permix/test/next/src-ppr/app/features/cache-safe-check.tsx
@@ -0,0 +1,13 @@
+import { tenant } from 'next/root-params'
+
+import { tenantPermix } from '../../lib/tenant-permix'
+
+export async function CacheSafeCheck() {
+ const current = await tenant()
+ const canCreate = await tenantPermix.check('post.create')
+ return (
+
+ {current}:{canCreate ? 'create-allowed' : 'create-denied'}
+
+ )
+}
diff --git a/permix/test/next/src-ppr/app/features/permission-holes.tsx b/permix/test/next/src-ppr/app/features/permission-holes.tsx
new file mode 100644
index 00000000..3a59d845
--- /dev/null
+++ b/permix/test/next/src-ppr/app/features/permission-holes.tsx
@@ -0,0 +1,21 @@
+import { Suspense } from 'react'
+
+import { PrivateEdit } from './private-edit'
+import { SessionIsland } from './session-island'
+
+export function PermissionHoles() {
+ return (
+ <>
+ loading session}
+ >
+
+
+ loading private}
+ >
+
+
+ >
+ )
+}
diff --git a/permix/test/next/src-ppr/app/features/private-edit.tsx b/permix/test/next/src-ppr/app/features/private-edit.tsx
new file mode 100644
index 00000000..1a6874cc
--- /dev/null
+++ b/permix/test/next/src-ppr/app/features/private-edit.tsx
@@ -0,0 +1,24 @@
+import { createPermix } from 'permix'
+
+import { getUser } from '../../lib/auth'
+import type { PostDefinition } from '../../lib/permix'
+import { rulesForUser } from '../../lib/permix'
+
+async function readPrivateEditPayload() {
+ 'use cache: private'
+ const user = await getUser()
+ const permix = createPermix()
+ permix.setup(rulesForUser(user))
+ if (!permix.check('post.update')) {
+ return null
+ }
+ return { canEdit: true as const, user }
+}
+
+export async function PrivateEdit() {
+ const payload = await readPrivateEditPayload()
+ if (!payload) {
+ return hidden
+ }
+ return {payload.user}:edit-allowed
+}
diff --git a/permix/test/next/src-ppr/app/features/session-island.tsx b/permix/test/next/src-ppr/app/features/session-island.tsx
new file mode 100644
index 00000000..c20c6b5f
--- /dev/null
+++ b/permix/test/next/src-ppr/app/features/session-island.tsx
@@ -0,0 +1,13 @@
+import { connection } from 'next/server'
+
+import { permix } from '../../lib/permix'
+
+export async function SessionIsland() {
+ await connection()
+ const allowed = await permix.check('post.create')
+ return (
+
+ {allowed ? 'session-allowed' : 'session-denied'}
+
+ )
+}
diff --git a/permix/test/next/src-ppr/lib/tenant-permix.ts b/permix/test/next/src-ppr/lib/tenant-permix.ts
new file mode 100644
index 00000000..9edb004c
--- /dev/null
+++ b/permix/test/next/src-ppr/lib/tenant-permix.ts
@@ -0,0 +1,15 @@
+import { tenant } from 'next/root-params'
+import { createPermix } from 'permix/next'
+
+import type { PostDefinition } from './permix'
+
+export const tenantPermix = createPermix(async () => {
+ const current = await tenant()
+ return {
+ post: {
+ create: current === 'acme',
+ read: true,
+ update: false,
+ },
+ }
+})
diff --git a/permix/test/next/src-ppr/next.config.mjs b/permix/test/next/src-ppr/next.config.mjs
new file mode 100644
index 00000000..c4404352
--- /dev/null
+++ b/permix/test/next/src-ppr/next.config.mjs
@@ -0,0 +1,18 @@
+const exposeTestingApi = process.env.EXPOSE_TESTING_API === '1'
+
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ transpilePackages: ['permix'],
+ cacheComponents: true,
+ partialPrefetching: true,
+ typescript: {
+ ignoreBuildErrors: true,
+ },
+ experimental: exposeTestingApi
+ ? {
+ exposeTestingApiInProductionBuild: true,
+ }
+ : undefined,
+}
+
+export default nextConfig
diff --git a/permix/test/next/src-ppr/root-params.d.ts b/permix/test/next/src-ppr/root-params.d.ts
new file mode 100644
index 00000000..0425bd01
--- /dev/null
+++ b/permix/test/next/src-ppr/root-params.d.ts
@@ -0,0 +1,3 @@
+declare module 'next/root-params' {
+ export function tenant(): Promise
+}
diff --git a/permix/test/next/src/app/actions.ts b/permix/test/next/src/app/actions.ts
new file mode 100644
index 00000000..2d8d5210
--- /dev/null
+++ b/permix/test/next/src/app/actions.ts
@@ -0,0 +1,13 @@
+'use server'
+
+import { createPermix } from 'permix'
+
+import { getUser } from '../lib/auth'
+import type { PostDefinition } from '../lib/permix'
+import { rulesForUser } from '../lib/permix'
+
+export async function checkCreate() {
+ const permix = createPermix()
+ permix.setup(rulesForUser(await getUser()))
+ return permix.check('post.create')
+}
diff --git a/permix/test/next/src/app/api/check/route.ts b/permix/test/next/src/app/api/check/route.ts
new file mode 100644
index 00000000..9640adf5
--- /dev/null
+++ b/permix/test/next/src/app/api/check/route.ts
@@ -0,0 +1,14 @@
+import { createPermix } from 'permix'
+
+import { getUser } from '../../../lib/auth'
+import type { PostDefinition } from '../../../lib/permix'
+import { rulesForUser } from '../../../lib/permix'
+
+export async function GET() {
+ const permix = createPermix()
+ permix.setup(rulesForUser(await getUser()))
+ return Response.json({
+ create: permix.check('post.create'),
+ read: permix.check('post.read'),
+ })
+}
diff --git a/permix/test/next/src/app/features/action-check.tsx b/permix/test/next/src/app/features/action-check.tsx
new file mode 100644
index 00000000..1350bd9e
--- /dev/null
+++ b/permix/test/next/src/app/features/action-check.tsx
@@ -0,0 +1,25 @@
+'use client'
+
+import { useState } from 'react'
+
+import { checkCreate } from '../actions'
+
+export function ActionCheck() {
+ const [result, setResult] = useState('idle')
+
+ return (
+
+ {
+ const allowed = await checkCreate()
+ setResult(allowed ? 'allowed' : 'denied')
+ }}
+ >
+ Check via action
+
+ {result}
+
+ )
+}
diff --git a/permix/test/next/src/app/features/concurrent-instances.tsx b/permix/test/next/src/app/features/concurrent-instances.tsx
new file mode 100644
index 00000000..040a7802
--- /dev/null
+++ b/permix/test/next/src/app/features/concurrent-instances.tsx
@@ -0,0 +1,16 @@
+import { instanceId } from '../../lib/instance-id'
+import { permix } from '../../lib/permix'
+
+export async function ConcurrentInstances() {
+ return (
+
+
+
+
+ )
+}
+
+async function InstanceSlot({ testId }: { testId: string }) {
+ const instance = await permix.getPermix()
+ return {String(instanceId(instance))}
+}
diff --git a/permix/test/next/src/app/features/public-read.tsx b/permix/test/next/src/app/features/public-read.tsx
new file mode 100644
index 00000000..30cfc296
--- /dev/null
+++ b/permix/test/next/src/app/features/public-read.tsx
@@ -0,0 +1,6 @@
+import { publicPermix } from '../../lib/permix'
+
+export async function PublicRead() {
+ const allowed = await publicPermix.check('post.read')
+ return {allowed ? 'allowed' : 'denied'}
+}
diff --git a/permix/test/next/src/app/features/session-create.tsx b/permix/test/next/src/app/features/session-create.tsx
new file mode 100644
index 00000000..600f66d5
--- /dev/null
+++ b/permix/test/next/src/app/features/session-create.tsx
@@ -0,0 +1,8 @@
+import { permix } from '../../lib/permix'
+
+export async function SessionCreate() {
+ const allowed = await permix.check('post.create')
+ return (
+ {allowed ? 'allowed' : 'denied'}
+ )
+}
diff --git a/permix/test/next/src/app/features/use-permix-create.tsx b/permix/test/next/src/app/features/use-permix-create.tsx
new file mode 100644
index 00000000..81daeca1
--- /dev/null
+++ b/permix/test/next/src/app/features/use-permix-create.tsx
@@ -0,0 +1,10 @@
+import { permix } from '../../lib/permix'
+
+export function UsePermixCreate() {
+ const instance = permix.usePermix()
+ return (
+
+ {instance.check('post.create') ? 'allowed' : 'denied'}
+
+ )
+}
diff --git a/permix/test/next/src/app/layout.tsx b/permix/test/next/src/app/layout.tsx
new file mode 100644
index 00000000..31b8e40b
--- /dev/null
+++ b/permix/test/next/src/app/layout.tsx
@@ -0,0 +1,16 @@
+import type { ReactNode } from 'react'
+
+export default function RootLayout({
+ children,
+}: {
+ children: ReactNode
+}) {
+ return (
+
+
+
+ {children}
+
+
+ )
+}
diff --git a/permix/test/next/src/app/page.tsx b/permix/test/next/src/app/page.tsx
new file mode 100644
index 00000000..426bab85
--- /dev/null
+++ b/permix/test/next/src/app/page.tsx
@@ -0,0 +1,33 @@
+import { Suspense } from 'react'
+
+import { ActionCheck } from './features/action-check'
+import { ConcurrentInstances } from './features/concurrent-instances'
+import { PublicRead } from './features/public-read'
+import { SessionCreate } from './features/session-create'
+import { UsePermixCreate } from './features/use-permix-create'
+
+export default function Home() {
+ return (
+
+ loading public}>
+
+
+ loading instances}
+ >
+
+
+ loading session}
+ >
+
+
+ loading usePermix}
+ >
+
+
+
+
+ )
+}
diff --git a/permix/test/next/src/lib/auth.ts b/permix/test/next/src/lib/auth.ts
new file mode 100644
index 00000000..129d5907
--- /dev/null
+++ b/permix/test/next/src/lib/auth.ts
@@ -0,0 +1,9 @@
+import { cookies } from 'next/headers'
+
+export type DemoUser = 'alice' | 'bob'
+
+export async function getUser(): Promise {
+ const store = await cookies()
+ const value = store.get('demo-user')?.value
+ return value === 'bob' ? 'bob' : 'alice'
+}
diff --git a/permix/test/next/src/lib/instance-id.ts b/permix/test/next/src/lib/instance-id.ts
new file mode 100644
index 00000000..8c490d67
--- /dev/null
+++ b/permix/test/next/src/lib/instance-id.ts
@@ -0,0 +1,12 @@
+const ids = new WeakMap()
+let sequence = 0
+
+export function instanceId(value: object): number {
+ const existing = ids.get(value)
+ if (existing !== undefined) {
+ return existing
+ }
+ sequence += 1
+ ids.set(value, sequence)
+ return sequence
+}
diff --git a/permix/test/next/src/lib/permix.ts b/permix/test/next/src/lib/permix.ts
new file mode 100644
index 00000000..cad941e3
--- /dev/null
+++ b/permix/test/next/src/lib/permix.ts
@@ -0,0 +1,29 @@
+import { createPermix } from 'permix/next'
+
+import { getUser } from './auth'
+
+export type PostDefinition = {
+ post: ['create', 'read', 'update']
+}
+
+export function rulesForUser(user: 'alice' | 'bob') {
+ return {
+ post: {
+ create: user === 'alice',
+ read: true,
+ update: user === 'alice',
+ },
+ } as const
+}
+
+export const permix = createPermix(async () =>
+ rulesForUser(await getUser())
+)
+
+export const publicPermix = createPermix(() => ({
+ post: {
+ create: false,
+ read: true,
+ update: false,
+ },
+}))
diff --git a/permix/test/next/src/next-env.d.ts b/permix/test/next/src/next-env.d.ts
new file mode 100644
index 00000000..6080addc
--- /dev/null
+++ b/permix/test/next/src/next-env.d.ts
@@ -0,0 +1,2 @@
+///
+///
diff --git a/permix/test/next/src/next.config.mjs b/permix/test/next/src/next.config.mjs
new file mode 100644
index 00000000..21c60fed
--- /dev/null
+++ b/permix/test/next/src/next.config.mjs
@@ -0,0 +1,12 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ transpilePackages: ['permix'],
+ typescript: {
+ ignoreBuildErrors: true,
+ },
+ eslint: {
+ ignoreDuringBuilds: true,
+ },
+}
+
+export default nextConfig
diff --git a/permix/test/next/src/package.json b/permix/test/next/src/package.json
new file mode 100644
index 00000000..72730e1d
--- /dev/null
+++ b/permix/test/next/src/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "permix-next-fixture",
+ "private": true,
+ "type": "module"
+}
diff --git a/permix/test/next/src/tsconfig.json b/permix/test/next/src/tsconfig.json
new file mode 100644
index 00000000..4fc71f3a
--- /dev/null
+++ b/permix/test/next/src/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "jsx": "react-jsx",
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "paths": {
+ "@/*": ["./*"]
+ },
+ "resolveJsonModule": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "isolatedModules": true,
+ "skipLibCheck": true,
+ "plugins": [{ "name": "next" }]
+ },
+ "include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/permix/test/next/tests/compat.spec.ts b/permix/test/next/tests/compat.spec.ts
new file mode 100644
index 00000000..9f4037be
--- /dev/null
+++ b/permix/test/next/tests/compat.spec.ts
@@ -0,0 +1,98 @@
+import { expect, test, type Page } from '@playwright/test'
+
+const homePath =
+ process.env.PERMIX_NEXT_VERSION === '16.3.3' ? '/acme' : '/'
+
+async function setUser(page: Page, user: 'alice' | 'bob') {
+ const baseURL = test.info().project.use.baseURL
+ if (!baseURL) {
+ throw new Error('BASE_URL is required')
+ }
+ const url = new URL(baseURL)
+ await page.context().addCookies([
+ {
+ name: 'demo-user',
+ value: user,
+ domain: url.hostname,
+ path: '/',
+ },
+ ])
+}
+
+test.describe('next adapter request state', () => {
+ test('concurrent RSC callers share one initialized instance', async ({
+ page,
+ }) => {
+ await setUser(page, 'alice')
+ await page.goto(homePath)
+ await expect(page.getByTestId('instance-a')).not.toHaveText('')
+ await expect(page.getByTestId('instance-b')).toHaveText(
+ await page.getByTestId('instance-a').innerText()
+ )
+ await expect(page.getByTestId('use-permix-create')).toHaveText('allowed')
+ await expect(page.getByTestId('session-create')).toHaveText('allowed')
+ await expect(page.getByTestId('public-read')).toHaveText('allowed')
+ })
+
+ test('concurrent requests stay isolated', async ({ browser, baseURL }) => {
+ if (!baseURL) {
+ throw new Error('BASE_URL is required')
+ }
+ const url = new URL(baseURL)
+
+ async function openAs(user: 'alice' | 'bob') {
+ const context = await browser.newContext()
+ await context.addCookies([
+ {
+ name: 'demo-user',
+ value: user,
+ domain: url.hostname,
+ path: '/',
+ },
+ ])
+ const page = await context.newPage()
+ await page.goto(homePath)
+ return { context, page }
+ }
+
+ const alice = await openAs('alice')
+ const bob = await openAs('bob')
+
+ await expect(alice.page.getByTestId('session-create')).toHaveText('allowed')
+ await expect(bob.page.getByTestId('session-create')).toHaveText('denied')
+ await expect(alice.page.getByTestId('use-permix-create')).toHaveText(
+ 'allowed'
+ )
+ await expect(bob.page.getByTestId('use-permix-create')).toHaveText('denied')
+
+ await alice.context.close()
+ await bob.context.close()
+ })
+
+ test('route handler sets up an explicit core instance', async ({
+ page,
+ baseURL,
+ }) => {
+ await setUser(page, 'alice')
+ const alice = await page.request.get(`${baseURL}/api/check`)
+ expect(await alice.json()).toEqual({ create: true, read: true })
+
+ await page.context().clearCookies()
+ await setUser(page, 'bob')
+ const bob = await page.request.get(`${baseURL}/api/check`)
+ expect(await bob.json()).toEqual({ create: false, read: true })
+ })
+
+ test('server action sets up an explicit core instance', async ({ page }) => {
+ await setUser(page, 'alice')
+ await page.goto(homePath)
+ await page.getByTestId('action-check').click()
+ await expect(page.getByTestId('action-result')).toHaveText('allowed')
+
+ await page.context().clearCookies()
+ await setUser(page, 'bob')
+ await page.reload()
+ await page.getByTestId('action-check').click()
+ await expect(page.getByTestId('action-result')).toHaveText('denied')
+ })
+})
diff --git a/permix/test/next/tests/instant.spec.ts b/permix/test/next/tests/instant.spec.ts
new file mode 100644
index 00000000..792c5cbf
--- /dev/null
+++ b/permix/test/next/tests/instant.spec.ts
@@ -0,0 +1,103 @@
+import { instant } from '@next/playwright'
+import { expect, test, type Page } from '@playwright/test'
+
+async function setUser(page: Page, user: 'alice' | 'bob') {
+ const baseURL = test.info().project.use.baseURL
+ if (!baseURL) {
+ throw new Error('BASE_URL is required')
+ }
+ const url = new URL(baseURL)
+ await page.context().addCookies([
+ {
+ name: 'demo-user',
+ value: user,
+ domain: url.hostname,
+ path: '/',
+ },
+ ])
+}
+
+function dashboardMain(page: Page) {
+ return page
+ .locator('main')
+ .filter({ has: page.getByTestId('dashboard-shell') })
+}
+
+test.describe('next 16.3 instant navigation', () => {
+ test('cache-safe permission UI is present under the lock', async ({
+ page,
+ baseURL,
+ }) => {
+ await setUser(page, 'alice')
+ await instant(
+ page,
+ async () => {
+ await page.goto('/acme')
+ await expect(page.getByTestId('app-shell')).toBeVisible()
+ await expect(page.getByTestId('tenant-shell')).toBeVisible()
+ await expect(page.getByText('acme:create-allowed')).toBeVisible()
+ },
+ { baseURL }
+ )
+ })
+
+ test('cold session island is gated then streams', async ({ page }) => {
+ await setUser(page, 'alice')
+ await page.goto('/acme')
+ await expect(page.getByTestId('tenant-home')).toBeVisible()
+
+ const dest = dashboardMain(page)
+ await instant(page, async () => {
+ await page.getByTestId('dashboard-link').click()
+ await expect(dest.getByTestId('dashboard-shell')).toBeVisible()
+ await expect(dest.getByTestId('session-island')).toHaveCount(0)
+ })
+
+ await expect(dest.getByTestId('session-island')).toBeVisible()
+ })
+
+ test('primed private payload is present immediately', async ({ page }) => {
+ await setUser(page, 'alice')
+ await page.goto('/acme')
+ await expect(page.getByTestId('private-edit')).toHaveText(
+ 'alice:edit-allowed'
+ )
+
+ const dest = dashboardMain(page)
+ await instant(page, async () => {
+ await page.getByTestId('dashboard-prefetch-link').click()
+ await expect(dest.getByTestId('dashboard-shell')).toBeVisible()
+ await expect(dest.getByTestId('private-edit')).toHaveText(
+ 'alice:edit-allowed'
+ )
+ })
+ })
+
+ test('default shared-shell navigation keeps tenant chrome instant', async ({
+ page,
+ }) => {
+ await setUser(page, 'alice')
+ await page.goto('/acme')
+ await expect(page.getByTestId('tenant-home')).toBeVisible()
+
+ await instant(page, async () => {
+ await page.getByTestId('globex-link').click()
+ await expect(page.getByTestId('tenant-shell')).toBeVisible()
+ await expect(page.getByText('globex:create-denied')).toBeVisible()
+ })
+ })
+
+ test('prefetch=true root-param navigation includes tenant-keyed UI', async ({
+ page,
+ }) => {
+ await setUser(page, 'alice')
+ await page.goto('/acme')
+ await expect(page.getByTestId('tenant-home')).toBeVisible()
+
+ await instant(page, async () => {
+ await page.getByTestId('globex-prefetch-link').click()
+ await expect(page.getByTestId('tenant-shell')).toBeVisible()
+ await expect(page.getByText('globex:create-denied')).toBeVisible()
+ })
+ })
+})
diff --git a/permix/vitest.config.ts b/permix/vitest.config.ts
index 987dc46a..8f175293 100644
--- a/permix/vitest.config.ts
+++ b/permix/vitest.config.ts
@@ -21,6 +21,11 @@ export default defineConfig({
],
test: {
environment: 'happy-dom',
- exclude: ['**/node_modules/**', '**/dist/**', '**/.svelte-kit/**'],
+ exclude: [
+ '**/node_modules/**',
+ '**/dist/**',
+ '**/.svelte-kit/**',
+ '**/test/next/**',
+ ],
},
})
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f12eb59a..cf576c11 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -107,19 +107,19 @@ importers:
version: 1.168.24(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vercel/analytics':
specifier: ^2.0.1
- version: 2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@7.0.2))
+ version: 2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@7.0.2))
fumadocs-core:
specifier: ^16.9.3
- version: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
+ version: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
fumadocs-mdx:
specifier: ^15.0.10
- version: 15.0.10(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 15.0.10(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
fumadocs-twoslash:
specifier: ^3.2.0
- version: 3.2.0(b4a625e5e99ec7f2a6e049efc2c17627)
+ version: 3.2.0(056b6d17c4e5ae10ed9baa01b99cdd99)
fumadocs-ui:
specifier: ^16.9.3
- version: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)
+ version: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)
mermaid:
specifier: ^11.15.0
version: 11.15.0
@@ -327,8 +327,8 @@ importers:
examples/next:
dependencies:
next:
- specifier: 16.2.6
- version: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ specifier: 16.3.3
+ version: 16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
permix:
specifier: workspace:*
version: link:../../permix
@@ -496,7 +496,7 @@ importers:
version: 1.167.1(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-router-ssr-query':
specifier: latest
- version: 1.167.1(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ version: 1.167.2(@tanstack/query-core@5.102.7)(@tanstack/react-query@5.102.7(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start':
specifier: latest
version: 1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -608,8 +608,8 @@ importers:
specifier: '>=4'
version: 4.12.23
next:
- specifier: '>=14'
- version: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ specifier: '>=15'
+ version: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
solid-js:
specifier: '>=1'
version: 1.9.13
@@ -708,6 +708,49 @@ importers:
specifier: ^4.4.3
version: 4.4.3
+ permix/test/next:
+ dependencies:
+ permix:
+ specifier: workspace:*
+ version: link:../..
+ react:
+ specifier: 'catalog:'
+ version: 19.2.6
+ react-dom:
+ specifier: 'catalog:'
+ version: 19.2.6(react@19.2.6)
+ devDependencies:
+ '@next/playwright':
+ specifier: 16.3.3
+ version: 16.3.3(@playwright/test@1.62.1)
+ '@playwright/test':
+ specifier: ^1.55.1
+ version: 1.62.1
+ '@types/node':
+ specifier: 'catalog:'
+ version: 25.9.1
+ '@types/react':
+ specifier: 'catalog:'
+ version: 19.2.15
+ '@types/react-dom':
+ specifier: 'catalog:'
+ version: 19.2.3(@types/react@19.2.15)
+ next-15:
+ specifier: npm:next@15.5.24
+ version: next@15.5.24(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ next-16-0:
+ specifier: npm:next@16.0.11
+ version: next@16.0.11(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ next-16-3:
+ specifier: npm:next@16.3.3
+ version: next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ typescript:
+ specifier: 'catalog:'
+ version: 7.0.2
+ typescript59:
+ specifier: catalog:typescript-classic
+ version: typescript@5.9.3
+
packages:
'@acemir/cssom@0.9.31':
@@ -925,6 +968,9 @@ packages:
'@emnapi/runtime@1.10.0':
resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
+ '@emnapi/runtime@1.11.3':
+ resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==}
+
'@emnapi/wasi-threads@1.2.1':
resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
@@ -1156,70 +1202,145 @@ packages:
cpu: [arm64]
os: [darwin]
+ '@img/sharp-darwin-arm64@0.35.4':
+ resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [darwin]
+
'@img/sharp-darwin-x64@0.34.5':
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [darwin]
+ '@img/sharp-darwin-x64@0.35.4':
+ resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-freebsd-wasm32@0.35.4':
+ resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==}
+ engines: {node: '>=20.9.0'}
+ os: [freebsd]
+
'@img/sharp-libvips-darwin-arm64@1.2.4':
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
cpu: [arm64]
os: [darwin]
+ '@img/sharp-libvips-darwin-arm64@1.3.3':
+ resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==}
+ cpu: [arm64]
+ os: [darwin]
+
'@img/sharp-libvips-darwin-x64@1.2.4':
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
cpu: [x64]
os: [darwin]
+ '@img/sharp-libvips-darwin-x64@1.3.3':
+ resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==}
+ cpu: [x64]
+ os: [darwin]
+
'@img/sharp-libvips-linux-arm64@1.2.4':
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64]
os: [linux]
libc: [glibc]
+ '@img/sharp-libvips-linux-arm64@1.3.3':
+ resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm]
os: [linux]
libc: [glibc]
+ '@img/sharp-libvips-linux-arm@1.3.3':
+ resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
+ '@img/sharp-libvips-linux-ppc64@1.3.3':
+ resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
+ '@img/sharp-libvips-linux-riscv64@1.3.3':
+ resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
+ '@img/sharp-libvips-linux-s390x@1.3.3':
+ resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64]
os: [linux]
libc: [glibc]
+ '@img/sharp-libvips-linux-x64@1.3.3':
+ resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64]
os: [linux]
libc: [musl]
+ '@img/sharp-libvips-linuxmusl-arm64@1.3.3':
+ resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64]
os: [linux]
libc: [musl]
+ '@img/sharp-libvips-linuxmusl-x64@1.3.3':
+ resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
'@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -1227,6 +1348,13 @@ packages:
os: [linux]
libc: [glibc]
+ '@img/sharp-linux-arm64@0.35.4':
+ resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -1234,6 +1362,13 @@ packages:
os: [linux]
libc: [glibc]
+ '@img/sharp-linux-arm@0.35.4':
+ resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -1241,6 +1376,13 @@ packages:
os: [linux]
libc: [glibc]
+ '@img/sharp-linux-ppc64@0.35.4':
+ resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==}
+ engines: {node: '>=20.9.0'}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -1248,6 +1390,13 @@ packages:
os: [linux]
libc: [glibc]
+ '@img/sharp-linux-riscv64@0.35.4':
+ resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -1255,6 +1404,13 @@ packages:
os: [linux]
libc: [glibc]
+ '@img/sharp-linux-s390x@0.35.4':
+ resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==}
+ engines: {node: '>=20.9.0'}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -1262,6 +1418,13 @@ packages:
os: [linux]
libc: [glibc]
+ '@img/sharp-linux-x64@0.35.4':
+ resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
'@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -1269,6 +1432,13 @@ packages:
os: [linux]
libc: [musl]
+ '@img/sharp-linuxmusl-arm64@0.35.4':
+ resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
'@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -1276,29 +1446,63 @@ packages:
os: [linux]
libc: [musl]
+ '@img/sharp-linuxmusl-x64@0.35.4':
+ resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
'@img/sharp-wasm32@0.34.5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
+ '@img/sharp-wasm32@0.35.4':
+ resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==}
+ engines: {node: '>=20.9.0'}
+
+ '@img/sharp-webcontainers-wasm32@0.35.4':
+ resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [wasm32]
+
'@img/sharp-win32-arm64@0.34.5':
resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [win32]
+ '@img/sharp-win32-arm64@0.35.4':
+ resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [win32]
+
'@img/sharp-win32-ia32@0.34.5':
resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
+ '@img/sharp-win32-ia32@0.35.4':
+ resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==}
+ engines: {node: ^20.9.0}
+ cpu: [ia32]
+ os: [win32]
+
'@img/sharp-win32-x64@0.34.5':
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [win32]
+ '@img/sharp-win32-x64@0.35.4':
+ resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [win32]
+
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
@@ -1340,21 +1544,88 @@ packages:
'@neodrag/core': 3.0.0-next.11
solid-js: ^1.0.0
+ '@next/env@15.5.24':
+ resolution: {integrity: sha512-mBDF7T0XKZjs9SpUAl0buizVO+O02ULjOvWX8o/AZo/5AGw/UAS1Zzcylmd4pqbftzmKQi+L/nB4jgBYKEAl5Q==}
+
+ '@next/env@16.0.11':
+ resolution: {integrity: sha512-hULMheQaOhFK1vAoFPigXca42LguwyLILtJKPRzpY1d+og6jk0YNAQVwLGNYYhWEMd2zj4gcIWSf1yC5PffqqA==}
+
'@next/env@16.2.6':
resolution: {integrity: sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==}
+ '@next/env@16.3.3':
+ resolution: {integrity: sha512-U2eYQRwXj+dsqxV79zFqExDdatnNY/ZWc2nsJU1p/OgT7fd3dXwlF6OjYaFQCfMoeTA19PWq+wVmYgimVA+V+g==}
+
+ '@next/playwright@16.3.3':
+ resolution: {integrity: sha512-Uha5vKciXER99u9J9ePBW6FQJxMklZp9K2r+xzW/KBvaS2ITvOsP/6BMyQBv2FJbitW5TQv+KNv72aqtnsikuw==}
+ peerDependencies:
+ '@playwright/test': '>=1.0.0'
+ peerDependenciesMeta:
+ '@playwright/test':
+ optional: true
+
+ '@next/swc-darwin-arm64@15.5.24':
+ resolution: {integrity: sha512-AGdNLvxZNY6eR2iSnV+6wUa8CiHTMr4F7g3uHH7fT4ICIJBE00R9u4tzN/Vuwsw0cOi8MTD2HJcTCb6siMH88Q==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@next/swc-darwin-arm64@16.0.11':
+ resolution: {integrity: sha512-3G7Rx6m6tgLqkc3Ce3QY/Yrsx7nJF4ithdHfx70Jmzel8m2xpjnGRC+oB4UcCHvQwN0ZP5YsLJakwx/M0vWbSQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
'@next/swc-darwin-arm64@16.2.6':
resolution: {integrity: sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
+ '@next/swc-darwin-arm64@16.3.3':
+ resolution: {integrity: sha512-8Hiv32QJPwdV6KYJ8meR9SBA061tQqnIKTJDocvOXlEQqib0xMFpzArosuffFUUc0sslbh7QQ8a3Yey1QV8EIw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@next/swc-darwin-x64@15.5.24':
+ resolution: {integrity: sha512-9HrQajBMmGcrrrvDfRimiCrbAPh3E6uHJmwBovYr6Yrmi9p9PZqI876BrXX280wICh3o2XwUlp4blkB0NNBqFg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@next/swc-darwin-x64@16.0.11':
+ resolution: {integrity: sha512-poUTsYKRwuG+eApDngouEiN6AGcAMq8TAQYP8Nou7iMS7x6+q3dFhhyhgodIzTF9acsEINl4cIzMaM9XJor8kw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
'@next/swc-darwin-x64@16.2.6':
resolution: {integrity: sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
+ '@next/swc-darwin-x64@16.3.3':
+ resolution: {integrity: sha512-A1lgKgwVchRYmSe467zdwhxT9040dd8lH+o65sL5Jet8fjB4kegw/rDyPIpYVRb6jAqwXFOJpjIXJLxQKLiE3A==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@next/swc-linux-arm64-gnu@15.5.24':
+ resolution: {integrity: sha512-rl9LSfE75si0WT3cDgdUC1XYCKS+TgxC+/IjitmeycrAG18X/plIP1/vy8dd/HPycYcIvE688PD7FuvEAiEAew==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@next/swc-linux-arm64-gnu@16.0.11':
+ resolution: {integrity: sha512-Q9shvB+eLNrK/n8w+/ZTWSzbEIzJ56mP83ZVaqmHay6/Ulcn6THEId4gxfYCXmSwEG/xPAtv58FBWeZkp36XUA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
'@next/swc-linux-arm64-gnu@16.2.6':
resolution: {integrity: sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==}
engines: {node: '>= 10'}
@@ -1362,6 +1633,27 @@ packages:
os: [linux]
libc: [glibc]
+ '@next/swc-linux-arm64-gnu@16.3.3':
+ resolution: {integrity: sha512-bf0FIssMFueU2dm7vQEWWxk0c8UjKTdW0yzuh0sQsD8pf1+KCLDdaqhYZNMYGmXwEOiHAUzgBKudovIlcvvBjg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@next/swc-linux-arm64-musl@15.5.24':
+ resolution: {integrity: sha512-TlNAnpsjxSF3aAUtqnfmtXXf8m9sIDBlmF3c7bTAlnshUYu2U0OxN2uf5d0gcFwqHVEdivJNBcCaqNOwPGNimw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@next/swc-linux-arm64-musl@16.0.11':
+ resolution: {integrity: sha512-rq+d/a0FZHVPEh3zismoQgfVkSIEzlTbNhD4Z8bToLMszUlggAh1D1syhJ4MHkYzXRszhjS2emy0PYXz7Uwttw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
'@next/swc-linux-arm64-musl@16.2.6':
resolution: {integrity: sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==}
engines: {node: '>= 10'}
@@ -1369,6 +1661,27 @@ packages:
os: [linux]
libc: [musl]
+ '@next/swc-linux-arm64-musl@16.3.3':
+ resolution: {integrity: sha512-W7viwCk9JY/cAkdz/A273rd5bb3RgT/IHwR7Upv90tunjBWNtAAhGhoecHh+teRNRSinuAFmE+l7fwZ4YKkrXg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@next/swc-linux-x64-gnu@15.5.24':
+ resolution: {integrity: sha512-7dwtlhr0SLndqTG1z9ncRkbJswDZiKWlxzFyXDvJ2RDZRDRHp8zyMJ4D9UH/FgnQeXxxB6gZy2pMcIUoNKQ4pA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@next/swc-linux-x64-gnu@16.0.11':
+ resolution: {integrity: sha512-82Wroterii1p15O+ZF/DDsHPuxKptR1JGK+obgbAk13vrc3B/fTJ2qOOmdeoMwAQ15gb/9mN4LQl9+IzFje76Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
'@next/swc-linux-x64-gnu@16.2.6':
resolution: {integrity: sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==}
engines: {node: '>= 10'}
@@ -1376,6 +1689,27 @@ packages:
os: [linux]
libc: [glibc]
+ '@next/swc-linux-x64-gnu@16.3.3':
+ resolution: {integrity: sha512-0W46zw1N3ODpI6n0GeivHvvob1pooozgZVqy65k0mh4/7vr+FbY9+WpHzNVXjHipJf/A3FDheBG19H1s5A25rA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@next/swc-linux-x64-musl@15.5.24':
+ resolution: {integrity: sha512-kGZxM+WhkYs0276lFrMkj7PRtXT3Btp6cwvfSO/cCVLxJJttB5Ccnl2niaCgUja8HgSbEVnMHpg3FJWoOJ9e/g==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@next/swc-linux-x64-musl@16.0.11':
+ resolution: {integrity: sha512-YK9RoeZuHWBd+wHi5/7VLp6P5ZOldAjQfBjjtzcR4f14FNmwT0a3ozMMlG2txDxh53krAd5yOO601RbJxH0gCQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
'@next/swc-linux-x64-musl@16.2.6':
resolution: {integrity: sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==}
engines: {node: '>= 10'}
@@ -1383,18 +1717,61 @@ packages:
os: [linux]
libc: [musl]
+ '@next/swc-linux-x64-musl@16.3.3':
+ resolution: {integrity: sha512-H4mBso8ZTMBPtdT0PN0pBx2ayTvQuTuvS6qT13d77yVFJXAPCxkyIhLTmdMaGTJs0krQYI/qpzdHijCeihXhbg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@next/swc-win32-arm64-msvc@15.5.24':
+ resolution: {integrity: sha512-jBDDkZ/qKAqkWivWDMkJSXUzbzV0QKRBKJjEHUAvSB97Hzw7NLzJ6yV56Lts/wjir7s4P31GYgpbS6ZL+hasAA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@next/swc-win32-arm64-msvc@16.0.11':
+ resolution: {integrity: sha512-pcDMpSckekV8xj2SSKO8PaqaJhrmDx84zUNip0kOWsT/ERhhDpnWkr6KXMqRXVp2y5CW9pp4LwOFdtpt3rhRgw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
'@next/swc-win32-arm64-msvc@16.2.6':
resolution: {integrity: sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
+ '@next/swc-win32-arm64-msvc@16.3.3':
+ resolution: {integrity: sha512-cTMUJpcEGmeywofCUfhR+rSsoE33+rVPnPEYNTNdLNlsOeEg/vktOsKUSTb28vUGqD2jkm4Zaskcwn7OCI6FQg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@next/swc-win32-x64-msvc@15.5.24':
+ resolution: {integrity: sha512-JqtwjvvorjacQ0spgjmUJoxySoYgPwdT1sFdQ0/zmW4iMlP2hjYlCoJIyS7o6Epb4Fug8eco3HXFoBDUCDeH7Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@next/swc-win32-x64-msvc@16.0.11':
+ resolution: {integrity: sha512-Zzo9NLLRzBSHw9zOGpER/gdc5rofZHLjR2OIUIfoBaN2Oo5zWRl43IF5rMSX2LX7MPLTx4Ww8+5lNHAhXgitnA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
'@next/swc-win32-x64-msvc@16.2.6':
resolution: {integrity: sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
+ '@next/swc-win32-x64-msvc@16.3.3':
+ resolution: {integrity: sha512-2VR4cTBzHXaBjnGsuH6GyJjENzQOmHeAh11uY1iUhjm3j5dEUrVJuUj+VL78jaGi/Dik8xS76zEj18BsFhlVZQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
'@noble/hashes@1.8.0':
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
engines: {node: ^14.21.3 || >=16}
@@ -1902,6 +2279,11 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
+ '@playwright/test@1.62.1':
+ resolution: {integrity: sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==}
+ engines: {node: '>=20'}
+ hasBin: true
+
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
@@ -2509,6 +2891,9 @@ packages:
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+ '@swc/helpers@0.5.23':
+ resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==}
+
'@tailwindcss/node@4.3.0':
resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==}
@@ -2660,8 +3045,8 @@ packages:
resolution: {integrity: sha512-jRjArJ8wcMmi49tByK6KCrJxPp8EqFuFH+ltDG6HTPizTrBCAXkJB3dTtTXurLJh67YsPLzU0wLPdOtWYDuGSw==}
hasBin: true
- '@tanstack/query-core@5.101.0':
- resolution: {integrity: sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==}
+ '@tanstack/query-core@5.102.7':
+ resolution: {integrity: sha512-4TH8KQrCLoNs9Zvl1LW1iaZ9cZx3dGTgfZ1BnLrcRxMNnX/toe/dGbfvF/DkvbzRyfLfpMKZkembz3o0rnugtw==}
'@tanstack/react-devtools@0.10.12':
resolution: {integrity: sha512-dgoz7TFm97Izo/D34z91PD0h+ufk+eBmoN9OgRHJlj/c7Ol5xpIP7bqLBNByjgt5paRE2eSu5AQHV92Ul+G6iw==}
@@ -2672,8 +3057,8 @@ packages:
react: '>=16.8'
react-dom: '>=16.8'
- '@tanstack/react-query@5.101.0':
- resolution: {integrity: sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==}
+ '@tanstack/react-query@5.102.7':
+ resolution: {integrity: sha512-bbd8T4jDIj9aPbNn11SsjNH8apKY4zspkrw63JRsNDXjpW5SRF7X0ipi7yrGd8DCs8QhYfhQJKqEj1YLru1DbQ==}
peerDependencies:
react: ^18 || ^19
@@ -2701,12 +3086,12 @@ packages:
'@tanstack/router-core':
optional: true
- '@tanstack/react-router-ssr-query@1.167.1':
- resolution: {integrity: sha512-W9j5JPnBikyafvuUfykFfHIWod58OAbAAa5leNkXBcoDoocghMmu6w9uZOmUZvAWT7CSvgj5tBUtF7CM2OoHXQ==}
+ '@tanstack/react-router-ssr-query@1.167.2':
+ resolution: {integrity: sha512-yRvy0VJ00R8huPuquk+qyYQGMpYRc/G33mc6/yZ4f7LaBZz9h/VbACjmLzhm/+6u5WSmUt6ouJJZnI1/5Npfag==}
engines: {node: '>=20.19'}
peerDependencies:
- '@tanstack/query-core': '>=5.90.0'
- '@tanstack/react-query': '>=5.90.0'
+ '@tanstack/query-core': '>=5.102.0'
+ '@tanstack/react-query': '>=5.102.0'
'@tanstack/react-router': '>=1.127.0'
react: '>=18.0.0 || >=19.0.0'
react-dom: '>=18.0.0 || >=19.0.0'
@@ -2994,11 +3379,11 @@ packages:
webpack:
optional: true
- '@tanstack/router-ssr-query-core@1.169.1':
- resolution: {integrity: sha512-rngux8s/3mPQzcjLYDLkNU31coYVyCgrVTfpdwqUdY5jIEHqGTXrO73DTkPR1PppwYUeVhmNCgl8TctRcnupjg==}
+ '@tanstack/router-ssr-query-core@1.169.2':
+ resolution: {integrity: sha512-7pO65Aiq/1+aS3Mb6vSGtIjzQ/YGv9JTfBbn2EYlHcZnJ4s4ZGCeTPI847geB1RfJqVhfwxM8bZgGM51mYkolg==}
engines: {node: '>=20.19'}
peerDependencies:
- '@tanstack/query-core': '>=5.90.0'
+ '@tanstack/query-core': '>=5.102.0'
'@tanstack/router-core': '>=1.127.0'
'@tanstack/router-utils@1.162.1':
@@ -4757,6 +5142,11 @@ packages:
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
engines: {node: '>= 0.8'}
+ fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -5650,44 +6040,112 @@ packages:
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
peerDependenciesMeta:
- '@emotion/is-prop-valid':
+ '@emotion/is-prop-valid':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+
+ mri@1.2.0:
+ resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
+ engines: {node: '>=4'}
+
+ mrmime@2.0.1:
+ resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+ engines: {node: '>=10'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ muggle-string@0.4.1:
+ resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+ nanoid@3.3.12:
+ resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ nanoid@3.3.18:
+ resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ negotiator@1.0.0:
+ resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
+ engines: {node: '>= 0.6'}
+
+ next-themes@0.4.6:
+ resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==}
+ peerDependencies:
+ react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
+
+ next@15.5.24:
+ resolution: {integrity: sha512-Y+xn8EQCoC3ZbsFPyzE+tE8XOdrWeUdUF7NeXbmg9DsgAxl5UYxlsrvgVESHTyTGigoTa1bCUrxn70F5bqt0Gw==}
+ engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ '@playwright/test': ^1.51.1
+ babel-plugin-react-compiler: '*'
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ '@playwright/test':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+ sass:
+ optional: true
+
+ next@16.0.11:
+ resolution: {integrity: sha512-Xlo2aFWaoypPzXr4PFLSNmxrzNptlp+hgxnG9Y2THYvHrvmXIuHUyNAWO6Q+F4rm4/bmTOukprXEyF/j4qsC2A==}
+ engines: {node: '>=20.9.0'}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ '@playwright/test': ^1.51.1
+ babel-plugin-react-compiler: '*'
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
optional: true
- react:
+ '@playwright/test':
optional: true
- react-dom:
+ babel-plugin-react-compiler:
+ optional: true
+ sass:
optional: true
- mri@1.2.0:
- resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
- engines: {node: '>=4'}
-
- mrmime@2.0.1:
- resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
- engines: {node: '>=10'}
-
- ms@2.1.3:
- resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
-
- muggle-string@0.4.1:
- resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
-
- nanoid@3.3.12:
- resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ next@16.2.6:
+ resolution: {integrity: sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==}
+ engines: {node: '>=20.9.0'}
hasBin: true
-
- negotiator@1.0.0:
- resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
- engines: {node: '>= 0.6'}
-
- next-themes@0.4.6:
- resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==}
peerDependencies:
- react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
- react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
+ '@opentelemetry/api': ^1.1.0
+ '@playwright/test': ^1.51.1
+ babel-plugin-react-compiler: '*'
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ '@playwright/test':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+ sass:
+ optional: true
- next@16.2.6:
- resolution: {integrity: sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==}
+ next@16.3.3:
+ resolution: {integrity: sha512-tuRTx1nQ/yVw83cwJBo9F+njGUgMn3UHQycreWHB8XsStvvAh1AthbI8/4IpKnFaF58F+iSiHejYOlMQ/eq83g==}
engines: {node: '>=20.9.0'}
hasBin: true
peerDependencies:
@@ -5943,6 +6401,16 @@ packages:
pkg-types@1.3.1:
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+ playwright-core@1.62.1:
+ resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==}
+ engines: {node: '>=20'}
+ hasBin: true
+
+ playwright@1.62.1:
+ resolution: {integrity: sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==}
+ engines: {node: '>=20'}
+ hasBin: true
+
pnpm-workspace-yaml@1.6.1:
resolution: {integrity: sha512-yTeZntGWi8m9WNuhoVsP0DpFc4sC1U0+rr/qR6Zi9n2g3sxXY+JfccjXjjruNz96tM8I09yaJUA86doRnNLkbg==}
@@ -5964,6 +6432,10 @@ packages:
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
engines: {node: ^10 || ^12 || >=14}
+ postcss@8.5.23:
+ resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==}
+ engines: {node: ^10 || ^12 || >=14}
+
postgres-array@2.0.0:
resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
engines: {node: '>=4'}
@@ -6266,6 +6738,11 @@ packages:
engines: {node: '>=10'}
hasBin: true
+ semver@7.8.5:
+ resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
send@1.2.1:
resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==}
engines: {node: '>= 18'}
@@ -6307,6 +6784,15 @@ packages:
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ sharp@0.35.4:
+ resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==}
+ engines: {node: '>=20.9.0'}
+ peerDependencies:
+ '@types/node': '*'
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -7364,6 +7850,11 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@emnapi/runtime@1.11.3':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
'@emnapi/wasi-threads@1.2.1':
dependencies:
tslib: 2.8.1
@@ -7514,95 +8005,199 @@ snapshots:
'@img/sharp-libvips-darwin-arm64': 1.2.4
optional: true
+ '@img/sharp-darwin-arm64@0.35.4':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.3.3
+ optional: true
+
'@img/sharp-darwin-x64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.2.4
optional: true
+ '@img/sharp-darwin-x64@0.35.4':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.3.3
+ optional: true
+
+ '@img/sharp-freebsd-wasm32@0.35.4':
+ dependencies:
+ '@img/sharp-wasm32': 0.35.4
+ optional: true
+
'@img/sharp-libvips-darwin-arm64@1.2.4':
optional: true
+ '@img/sharp-libvips-darwin-arm64@1.3.3':
+ optional: true
+
'@img/sharp-libvips-darwin-x64@1.2.4':
optional: true
+ '@img/sharp-libvips-darwin-x64@1.3.3':
+ optional: true
+
'@img/sharp-libvips-linux-arm64@1.2.4':
optional: true
+ '@img/sharp-libvips-linux-arm64@1.3.3':
+ optional: true
+
'@img/sharp-libvips-linux-arm@1.2.4':
optional: true
+ '@img/sharp-libvips-linux-arm@1.3.3':
+ optional: true
+
'@img/sharp-libvips-linux-ppc64@1.2.4':
optional: true
+ '@img/sharp-libvips-linux-ppc64@1.3.3':
+ optional: true
+
'@img/sharp-libvips-linux-riscv64@1.2.4':
optional: true
+ '@img/sharp-libvips-linux-riscv64@1.3.3':
+ optional: true
+
'@img/sharp-libvips-linux-s390x@1.2.4':
optional: true
+ '@img/sharp-libvips-linux-s390x@1.3.3':
+ optional: true
+
'@img/sharp-libvips-linux-x64@1.2.4':
optional: true
+ '@img/sharp-libvips-linux-x64@1.3.3':
+ optional: true
+
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
optional: true
+ '@img/sharp-libvips-linuxmusl-arm64@1.3.3':
+ optional: true
+
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
optional: true
+ '@img/sharp-libvips-linuxmusl-x64@1.3.3':
+ optional: true
+
'@img/sharp-linux-arm64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.2.4
optional: true
+ '@img/sharp-linux-arm64@0.35.4':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.3.3
+ optional: true
+
'@img/sharp-linux-arm@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.2.4
optional: true
+ '@img/sharp-linux-arm@0.35.4':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.3.3
+ optional: true
+
'@img/sharp-linux-ppc64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-ppc64': 1.2.4
optional: true
+ '@img/sharp-linux-ppc64@0.35.4':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-ppc64': 1.3.3
+ optional: true
+
'@img/sharp-linux-riscv64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-riscv64': 1.2.4
optional: true
+ '@img/sharp-linux-riscv64@0.35.4':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-riscv64': 1.3.3
+ optional: true
+
'@img/sharp-linux-s390x@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.2.4
optional: true
+ '@img/sharp-linux-s390x@0.35.4':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.3.3
+ optional: true
+
'@img/sharp-linux-x64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.2.4
optional: true
+ '@img/sharp-linux-x64@0.35.4':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.3.3
+ optional: true
+
'@img/sharp-linuxmusl-arm64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
optional: true
+ '@img/sharp-linuxmusl-arm64@0.35.4':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.3.3
+ optional: true
+
'@img/sharp-linuxmusl-x64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
optional: true
+ '@img/sharp-linuxmusl-x64@0.35.4':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.3.3
+ optional: true
+
'@img/sharp-wasm32@0.34.5':
dependencies:
'@emnapi/runtime': 1.10.0
optional: true
+ '@img/sharp-wasm32@0.35.4':
+ dependencies:
+ '@emnapi/runtime': 1.11.3
+ optional: true
+
+ '@img/sharp-webcontainers-wasm32@0.35.4':
+ dependencies:
+ '@img/sharp-wasm32': 0.35.4
+ optional: true
+
'@img/sharp-win32-arm64@0.34.5':
optional: true
+ '@img/sharp-win32-arm64@0.35.4':
+ optional: true
+
'@img/sharp-win32-ia32@0.34.5':
optional: true
+ '@img/sharp-win32-ia32@0.35.4':
+ optional: true
+
'@img/sharp-win32-x64@0.34.5':
optional: true
+ '@img/sharp-win32-x64@0.35.4':
+ optional: true
+
'@isaacs/cliui@8.0.2':
dependencies:
string-width: 5.1.2
@@ -7679,32 +8274,114 @@ snapshots:
'@neodrag/core': 3.0.0-next.11
solid-js: 1.9.13
+ '@next/env@15.5.24': {}
+
+ '@next/env@16.0.11': {}
+
'@next/env@16.2.6': {}
+ '@next/env@16.3.3': {}
+
+ '@next/playwright@16.3.3(@playwright/test@1.62.1)':
+ optionalDependencies:
+ '@playwright/test': 1.62.1
+
+ '@next/swc-darwin-arm64@15.5.24':
+ optional: true
+
+ '@next/swc-darwin-arm64@16.0.11':
+ optional: true
+
'@next/swc-darwin-arm64@16.2.6':
optional: true
+ '@next/swc-darwin-arm64@16.3.3':
+ optional: true
+
+ '@next/swc-darwin-x64@15.5.24':
+ optional: true
+
+ '@next/swc-darwin-x64@16.0.11':
+ optional: true
+
'@next/swc-darwin-x64@16.2.6':
optional: true
+ '@next/swc-darwin-x64@16.3.3':
+ optional: true
+
+ '@next/swc-linux-arm64-gnu@15.5.24':
+ optional: true
+
+ '@next/swc-linux-arm64-gnu@16.0.11':
+ optional: true
+
'@next/swc-linux-arm64-gnu@16.2.6':
optional: true
+ '@next/swc-linux-arm64-gnu@16.3.3':
+ optional: true
+
+ '@next/swc-linux-arm64-musl@15.5.24':
+ optional: true
+
+ '@next/swc-linux-arm64-musl@16.0.11':
+ optional: true
+
'@next/swc-linux-arm64-musl@16.2.6':
optional: true
+ '@next/swc-linux-arm64-musl@16.3.3':
+ optional: true
+
+ '@next/swc-linux-x64-gnu@15.5.24':
+ optional: true
+
+ '@next/swc-linux-x64-gnu@16.0.11':
+ optional: true
+
'@next/swc-linux-x64-gnu@16.2.6':
optional: true
+ '@next/swc-linux-x64-gnu@16.3.3':
+ optional: true
+
+ '@next/swc-linux-x64-musl@15.5.24':
+ optional: true
+
+ '@next/swc-linux-x64-musl@16.0.11':
+ optional: true
+
'@next/swc-linux-x64-musl@16.2.6':
optional: true
+ '@next/swc-linux-x64-musl@16.3.3':
+ optional: true
+
+ '@next/swc-win32-arm64-msvc@15.5.24':
+ optional: true
+
+ '@next/swc-win32-arm64-msvc@16.0.11':
+ optional: true
+
'@next/swc-win32-arm64-msvc@16.2.6':
optional: true
+ '@next/swc-win32-arm64-msvc@16.3.3':
+ optional: true
+
+ '@next/swc-win32-x64-msvc@15.5.24':
+ optional: true
+
+ '@next/swc-win32-x64-msvc@16.0.11':
+ optional: true
+
'@next/swc-win32-x64-msvc@16.2.6':
optional: true
+ '@next/swc-win32-x64-msvc@16.3.3':
+ optional: true
+
'@noble/hashes@1.8.0': {}
'@nodelib/fs.scandir@2.1.5':
@@ -8042,6 +8719,10 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
+ '@playwright/test@1.62.1':
+ dependencies:
+ playwright: 1.62.1
+
'@polka/url@1.0.0-next.29': {}
'@quansync/fs@1.0.0':
@@ -8607,6 +9288,10 @@ snapshots:
dependencies:
tslib: 2.8.1
+ '@swc/helpers@0.5.23':
+ dependencies:
+ tslib: 2.8.1
+
'@tailwindcss/node@4.3.0':
dependencies:
'@jridgewell/remapping': 2.3.5
@@ -8767,7 +9452,7 @@ snapshots:
semver: 7.8.1
yaml: 2.8.3
- '@tanstack/query-core@5.101.0': {}
+ '@tanstack/query-core@5.102.7': {}
'@tanstack/react-devtools@0.10.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(solid-js@1.9.13)':
dependencies:
@@ -8782,9 +9467,9 @@ snapshots:
- solid-js
- utf-8-validate
- '@tanstack/react-query@5.101.0(react@19.2.6)':
+ '@tanstack/react-query@5.102.7(react@19.2.6)':
dependencies:
- '@tanstack/query-core': 5.101.0
+ '@tanstack/query-core': 5.102.7
react: 19.2.6
'@tanstack/react-router-devtools@1.167.0(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
@@ -8809,12 +9494,12 @@ snapshots:
transitivePeerDependencies:
- csstype
- '@tanstack/react-router-ssr-query@1.167.1(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
+ '@tanstack/react-router-ssr-query@1.167.2(@tanstack/query-core@5.102.7)(@tanstack/react-query@5.102.7(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
- '@tanstack/query-core': 5.101.0
- '@tanstack/react-query': 5.101.0(react@19.2.6)
+ '@tanstack/query-core': 5.102.7
+ '@tanstack/react-query': 5.102.7(react@19.2.6)
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/router-ssr-query-core': 1.169.1(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)
+ '@tanstack/router-ssr-query-core': 1.169.2(@tanstack/query-core@5.102.7)(@tanstack/router-core@1.171.27)
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
transitivePeerDependencies:
@@ -9206,9 +9891,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tanstack/router-ssr-query-core@1.169.1(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)':
+ '@tanstack/router-ssr-query-core@1.169.2(@tanstack/query-core@5.102.7)(@tanstack/router-core@1.171.27)':
dependencies:
- '@tanstack/query-core': 5.101.0
+ '@tanstack/query-core': 5.102.7
'@tanstack/router-core': 1.171.27
'@tanstack/router-utils@1.162.1(supports-color@7.2.0)':
@@ -9832,10 +10517,10 @@ snapshots:
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
- '@vercel/analytics@2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@7.0.2))':
+ '@vercel/analytics@2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@7.0.2))':
optionalDependencies:
'@sveltejs/kit': 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@7.0.2)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- next: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ next: 16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
svelte: 5.56.0
vue: 3.5.35(typescript@7.0.2)
@@ -9942,7 +10627,7 @@ snapshots:
'@vue/shared': 3.5.35
estree-walker: 2.0.2
magic-string: 0.30.21
- postcss: 8.5.15
+ postcss: 8.5.23
source-map-js: 1.2.1
'@vue/compiler-ssr@3.5.35':
@@ -10962,10 +11647,13 @@ snapshots:
fresh@2.0.0: {}
+ fsevents@2.3.2:
+ optional: true
+
fsevents@2.3.3:
optional: true
- fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3):
+ fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3):
dependencies:
'@orama/orama': 3.1.18
estree-util-value-to-estree: 3.5.0
@@ -10992,21 +11680,21 @@ snapshots:
'@types/mdast': 4.0.4
'@types/react': 19.2.15
lucide-react: 1.17.0(react@19.2.6)
- next: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ next: 16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
zod: 4.4.3
transitivePeerDependencies:
- supports-color
- fumadocs-mdx@15.0.10(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ fumadocs-mdx@15.0.10(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(supports-color@7.2.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
'@mdx-js/mdx': 3.1.1(supports-color@7.2.0)
'@standard-schema/spec': 1.1.0
chokidar: 5.0.0
esbuild: 0.28.0
estree-util-value-to-estree: 3.5.0
- fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
+ fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
js-yaml: 4.1.1
mdast-util-mdx: 3.0.0(supports-color@7.2.0)
picocolors: 1.1.1
@@ -11022,19 +11710,19 @@ snapshots:
'@types/mdast': 4.0.4
'@types/mdx': 2.0.13
'@types/react': 19.2.15
- next: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ next: 16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
rolldown: 1.0.3
vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- supports-color
- fumadocs-twoslash@3.2.0(b4a625e5e99ec7f2a6e049efc2c17627):
+ fumadocs-twoslash@3.2.0(056b6d17c4e5ae10ed9baa01b99cdd99):
dependencies:
'@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@shikijs/twoslash': 4.1.0(supports-color@7.2.0)(typescript@7.0.2)
- fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
- fumadocs-ui: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)
+ fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
+ fumadocs-ui: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)
mdast-util-from-markdown: 2.0.3(supports-color@7.2.0)
mdast-util-gfm: 3.1.0(supports-color@7.2.0)
mdast-util-to-hast: 13.2.1
@@ -11050,7 +11738,7 @@ snapshots:
- supports-color
- typescript
- fumadocs-ui@16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0):
+ fumadocs-ui@16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0):
dependencies:
'@fumadocs/tailwind': 0.0.5(@tailwindcss/oxide@4.3.0)(tailwindcss@4.3.0)
'@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -11064,7 +11752,7 @@ snapshots:
'@radix-ui/react-slot': 1.2.4(@types/react@19.2.15)(react@19.2.6)
'@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
class-variance-authority: 0.7.1
- fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
+ fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1(supports-color@7.2.0))(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(supports-color@7.2.0)(zod@4.4.3)
lucide-react: 1.17.0(react@19.2.6)
motion: 12.40.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
next-themes: 0.4.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -11079,7 +11767,7 @@ snapshots:
optionalDependencies:
'@types/mdx': 2.0.13
'@types/react': 19.2.15
- next: 16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ next: 16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
transitivePeerDependencies:
- '@emotion/is-prop-valid'
- '@tailwindcss/oxide'
@@ -11616,7 +12304,7 @@ snapshots:
make-dir@4.0.0:
dependencies:
- semver: 7.8.1
+ semver: 7.8.5
markdown-extensions@2.0.0: {}
@@ -12155,6 +12843,8 @@ snapshots:
nanoid@3.3.12: {}
+ nanoid@3.3.18: {}
+
negotiator@1.0.0: {}
next-themes@0.4.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
@@ -12162,7 +12852,55 @@ snapshots:
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
+ next@15.5.24(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
+ dependencies:
+ '@next/env': 15.5.24
+ '@swc/helpers': 0.5.15
+ caniuse-lite: 1.0.30001793
+ postcss: 8.4.31
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.2.6)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 15.5.24
+ '@next/swc-darwin-x64': 15.5.24
+ '@next/swc-linux-arm64-gnu': 15.5.24
+ '@next/swc-linux-arm64-musl': 15.5.24
+ '@next/swc-linux-x64-gnu': 15.5.24
+ '@next/swc-linux-x64-musl': 15.5.24
+ '@next/swc-win32-arm64-msvc': 15.5.24
+ '@next/swc-win32-x64-msvc': 15.5.24
+ '@playwright/test': 1.62.1
+ sharp: 0.34.5
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
+ next@16.0.11(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
+ dependencies:
+ '@next/env': 16.0.11
+ '@swc/helpers': 0.5.15
+ caniuse-lite: 1.0.30001793
+ postcss: 8.4.31
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.2.6)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 16.0.11
+ '@next/swc-darwin-x64': 16.0.11
+ '@next/swc-linux-arm64-gnu': 16.0.11
+ '@next/swc-linux-arm64-musl': 16.0.11
+ '@next/swc-linux-x64-gnu': 16.0.11
+ '@next/swc-linux-x64-musl': 16.0.11
+ '@next/swc-win32-arm64-msvc': 16.0.11
+ '@next/swc-win32-x64-msvc': 16.0.11
+ '@playwright/test': 1.62.1
+ sharp: 0.34.5
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
+ next@16.2.6(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
dependencies:
'@next/env': 16.2.6
'@swc/helpers': 0.5.15
@@ -12181,11 +12919,38 @@ snapshots:
'@next/swc-linux-x64-musl': 16.2.6
'@next/swc-win32-arm64-msvc': 16.2.6
'@next/swc-win32-x64-msvc': 16.2.6
+ '@playwright/test': 1.62.1
sharp: 0.34.5
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
+ next@16.3.3(@babel/core@7.29.7(supports-color@7.2.0))(@playwright/test@1.62.1)(@types/node@25.9.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
+ dependencies:
+ '@next/env': 16.3.3
+ '@swc/helpers': 0.5.23
+ baseline-browser-mapping: 2.10.33
+ caniuse-lite: 1.0.30001793
+ postcss: 8.5.23
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.2.6)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 16.3.3
+ '@next/swc-darwin-x64': 16.3.3
+ '@next/swc-linux-arm64-gnu': 16.3.3
+ '@next/swc-linux-arm64-musl': 16.3.3
+ '@next/swc-linux-x64-gnu': 16.3.3
+ '@next/swc-linux-x64-musl': 16.3.3
+ '@next/swc-win32-arm64-msvc': 16.3.3
+ '@next/swc-win32-x64-msvc': 16.3.3
+ '@playwright/test': 1.62.1
+ sharp: 0.35.4(@types/node@25.9.1)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - '@types/node'
+ - babel-plugin-macros
+
nf3@0.3.17: {}
nitro@3.0.260522-beta(chokidar@5.0.0)(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3))(jiti@2.7.0)(lru-cache@11.5.1)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
@@ -12490,6 +13255,14 @@ snapshots:
mlly: 1.8.2
pathe: 2.0.3
+ playwright-core@1.62.1: {}
+
+ playwright@1.62.1:
+ dependencies:
+ playwright-core: 1.62.1
+ optionalDependencies:
+ fsevents: 2.3.2
+
pnpm-workspace-yaml@1.6.1:
dependencies:
yaml: 2.9.0
@@ -12518,6 +13291,12 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ postcss@8.5.23:
+ dependencies:
+ nanoid: 3.3.18
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
postgres-array@2.0.0: {}
postgres-bytea@1.0.1: {}
@@ -12848,6 +13627,8 @@ snapshots:
semver@7.8.1: {}
+ semver@7.8.5: {}
+
send@1.2.1(supports-color@7.2.0):
dependencies:
debug: 4.4.3(supports-color@7.2.0)
@@ -12923,6 +13704,40 @@ snapshots:
'@img/sharp-win32-x64': 0.34.5
optional: true
+ sharp@0.35.4(@types/node@25.9.1):
+ dependencies:
+ '@img/colour': 1.1.0
+ detect-libc: 2.1.2
+ semver: 7.8.5
+ optionalDependencies:
+ '@img/sharp-darwin-arm64': 0.35.4
+ '@img/sharp-darwin-x64': 0.35.4
+ '@img/sharp-freebsd-wasm32': 0.35.4
+ '@img/sharp-libvips-darwin-arm64': 1.3.3
+ '@img/sharp-libvips-darwin-x64': 1.3.3
+ '@img/sharp-libvips-linux-arm': 1.3.3
+ '@img/sharp-libvips-linux-arm64': 1.3.3
+ '@img/sharp-libvips-linux-ppc64': 1.3.3
+ '@img/sharp-libvips-linux-riscv64': 1.3.3
+ '@img/sharp-libvips-linux-s390x': 1.3.3
+ '@img/sharp-libvips-linux-x64': 1.3.3
+ '@img/sharp-libvips-linuxmusl-arm64': 1.3.3
+ '@img/sharp-libvips-linuxmusl-x64': 1.3.3
+ '@img/sharp-linux-arm': 0.35.4
+ '@img/sharp-linux-arm64': 0.35.4
+ '@img/sharp-linux-ppc64': 0.35.4
+ '@img/sharp-linux-riscv64': 0.35.4
+ '@img/sharp-linux-s390x': 0.35.4
+ '@img/sharp-linux-x64': 0.35.4
+ '@img/sharp-linuxmusl-arm64': 0.35.4
+ '@img/sharp-linuxmusl-x64': 0.35.4
+ '@img/sharp-webcontainers-wasm32': 0.35.4
+ '@img/sharp-win32-arm64': 0.35.4
+ '@img/sharp-win32-ia32': 0.35.4
+ '@img/sharp-win32-x64': 0.35.4
+ '@types/node': 25.9.1
+ optional: true
+
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 49e0d1be..75dad6c4 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,5 +1,6 @@
packages:
- permix
+ - permix/test/next
- docs
- examples/*
allowBuilds:
From 0fa844e6a6eb5d74a9e36fcb8d39114a82537d56 Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 13:06:51 +0200
Subject: [PATCH 04/28] fix: read hydrated permissions on first Solid, Svelte,
and Vue render
Subscribe to setup/ready during render instead of after an effect so dehydrated booleans and already-initialized rules are visible on the first paint.
Co-authored-by: Cursor
---
permix/src/solid/components.test.tsx | 37 ++++++++++++++++
permix/src/solid/components.tsx | 9 +---
permix/src/solid/hooks.test.tsx | 29 +++++++++++++
.../__fixtures__/HydrateConsumer.svelte | 1 +
permix/src/svelte/components.test.ts | 25 +++++++++++
permix/src/svelte/context.svelte.ts | 22 +++++-----
permix/src/svelte/hooks.test.ts | 18 ++++++++
permix/src/vue/components.test.ts | 42 +++++++++++++++++++
permix/src/vue/composables.test.ts | 24 +++++++++++
9 files changed, 188 insertions(+), 19 deletions(-)
diff --git a/permix/src/solid/components.test.tsx b/permix/src/solid/components.test.tsx
index bbe8f56a..484a98e7 100644
--- a/permix/src/solid/components.test.tsx
+++ b/permix/src/solid/components.test.tsx
@@ -41,6 +41,43 @@ describe('components', () => {
expect(container.firstChild).toHaveTextContent('true')
})
+ it('uses dehydrated rules on the first render', () => {
+ const permixServer = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ permixServer.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ const dehydrated = permixServer.dehydrate()
+ const permixClient = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ const TestComponent = () => {
+ const { check, isReady } = usePermix(permixClient)
+ return (
+
+ {check('post.create').toString()}:{isReady().toString()}
+
+ )
+ }
+
+ const { container } = render(() => (
+
+
+
+
+
+ ))
+
+ expect(container.firstChild).toHaveTextContent('true:false')
+ })
+
it('should work with Check component', () => {
const permix = createPermix<{
post: ['create']
diff --git a/permix/src/solid/components.tsx b/permix/src/solid/components.tsx
index cf12e87a..1d5b2cd5 100644
--- a/permix/src/solid/components.tsx
+++ b/permix/src/solid/components.tsx
@@ -1,10 +1,5 @@
import type { JSX } from 'solid-js'
-import {
- createEffect,
- createMemo,
- createRenderEffect,
- onCleanup,
-} from 'solid-js'
+import { createMemo, createRenderEffect, onCleanup } from 'solid-js'
import { createStore } from 'solid-js/store'
import type {
@@ -33,7 +28,7 @@ export function PermixProvider(props: {
rules: props.permix.getRules(),
})
- createEffect(() => {
+ createRenderEffect(() => {
const setup = props.permix.hook('setup', () => {
setContext('rules', props.permix.getRules())
})
diff --git a/permix/src/solid/hooks.test.tsx b/permix/src/solid/hooks.test.tsx
index fb84133b..ab1b63f6 100644
--- a/permix/src/solid/hooks.test.tsx
+++ b/permix/src/solid/hooks.test.tsx
@@ -30,6 +30,35 @@ describe('permix solid', () => {
expect(result.check('post.read')).toBe(false)
})
+ it('reads ready state on the first render when setup ran before subscribe', () => {
+ const permix = createPermix<{
+ post: ['create']
+ }>()
+
+ permix.setup({
+ post: {
+ create: true,
+ },
+ })
+
+ const TestComponent = () => {
+ const { isReady, check } = usePermix(permix)
+ return (
+
+ {isReady().toString()}:{check('post.create').toString()}
+
+ )
+ }
+
+ const { container } = render(() => , {
+ wrapper: (props) => (
+ {props.children}
+ ),
+ })
+
+ expect(container.firstChild).toHaveTextContent('true:true')
+ })
+
it('should work with DOM rerender', async () => {
const permix = createPermix<{
post: [{ name: 'create'; type: { id: string } }, 'read']
diff --git a/permix/src/svelte/__fixtures__/HydrateConsumer.svelte b/permix/src/svelte/__fixtures__/HydrateConsumer.svelte
index d00a05bb..90e92694 100644
--- a/permix/src/svelte/__fixtures__/HydrateConsumer.svelte
+++ b/permix/src/svelte/__fixtures__/HydrateConsumer.svelte
@@ -9,3 +9,4 @@ const permissions = usePermix(permix)
{permissions.check('post.create').toString()}
+{permissions.isReady.toString()}
diff --git a/permix/src/svelte/components.test.ts b/permix/src/svelte/components.test.ts
index a999f398..366b15a1 100644
--- a/permix/src/svelte/components.test.ts
+++ b/permix/src/svelte/components.test.ts
@@ -35,6 +35,31 @@ describe('components', () => {
expect(getByTestId('create')).toHaveTextContent('true')
})
+ it('uses dehydrated rules on the first render', () => {
+ const permixServer = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ permixServer.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ const dehydrated = permixServer.dehydrate()
+ const permixClient = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ const { getByTestId } = render(HydrateApp, {
+ props: { permix: permixClient, state: dehydrated },
+ })
+
+ expect(getByTestId('create')).toHaveTextContent('true')
+ expect(getByTestId('ready')).toHaveTextContent('false')
+ })
+
it('should work with Check component', () => {
const permix = createPermix<{
post: ['create']
diff --git a/permix/src/svelte/context.svelte.ts b/permix/src/svelte/context.svelte.ts
index 14ab311f..bf98e819 100644
--- a/permix/src/svelte/context.svelte.ts
+++ b/permix/src/svelte/context.svelte.ts
@@ -1,4 +1,4 @@
-import { getContext, setContext } from 'svelte'
+import { getContext, onDestroy, setContext } from 'svelte'
import type { Definition, Permix, Rules } from '../core'
import { createCheck } from '../core'
@@ -27,18 +27,16 @@ export function providePermix(permix: Permix): void {
setContext(PERMIX_CONTEXT_KEY, context)
- $effect(() => {
- const setup = permix.hook('setup', () => {
- context.rules = permix.getRules()
- })
- const ready = permix.hook('ready', () => {
- context.isReady = permix.isReady()
- })
+ const setup = permix.hook('setup', () => {
+ context.rules = permix.getRules()
+ })
+ const ready = permix.hook('ready', () => {
+ context.isReady = permix.isReady()
+ })
- return () => {
- setup()
- ready()
- }
+ onDestroy(() => {
+ setup()
+ ready()
})
}
diff --git a/permix/src/svelte/hooks.test.ts b/permix/src/svelte/hooks.test.ts
index 8e37ca67..88f53366 100644
--- a/permix/src/svelte/hooks.test.ts
+++ b/permix/src/svelte/hooks.test.ts
@@ -25,6 +25,24 @@ describe('permix svelte', () => {
expect(getByTestId('read')).toHaveTextContent('false')
})
+ it('reads ready state on the first render when setup ran before subscribe', () => {
+ const permix = createPermix<{
+ post: [{ name: 'create'; type: { id: string } }, 'read']
+ }>()
+
+ permix.setup({
+ post: {
+ create: () => true,
+ read: true,
+ },
+ })
+
+ const { getByTestId } = render(HookApp, { props: { permix } })
+
+ expect(getByTestId('ready')).toHaveTextContent('true')
+ expect(getByTestId('read')).toHaveTextContent('true')
+ })
+
it('should work with DOM rerender', async () => {
const permix = createPermix<{
post: [{ name: 'create'; type: { id: string } }, 'read']
diff --git a/permix/src/vue/components.test.ts b/permix/src/vue/components.test.ts
index 83508d29..769e61e6 100644
--- a/permix/src/vue/components.test.ts
+++ b/permix/src/vue/components.test.ts
@@ -50,6 +50,48 @@ describe('components', () => {
expect(wrapper.text()).toBe('true')
})
+ it('uses dehydrated rules on the first render', () => {
+ const permixServer = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ permixServer.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ const dehydrated = permixServer.dehydrate()
+ const permixClient = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ const TestComponent = {
+ template: "{{ check('post.create') }}:{{ isReady }}
",
+ setup() {
+ const { check, isReady } = usePermix(permixClient)
+ return { check, isReady }
+ },
+ }
+
+ const wrapper = mount({
+ template: `
+
+
+
+
+
+ `,
+ components: { PermixProvider, PermixHydrate, TestComponent },
+ setup() {
+ return { permix: permixClient, dehydrated }
+ },
+ })
+
+ expect(wrapper.text()).toBe('true:false')
+ })
+
it('should work with Check component', () => {
const permix = createPermix<{
post: ['create']
diff --git a/permix/src/vue/composables.test.ts b/permix/src/vue/composables.test.ts
index e9807aaf..341fb560 100644
--- a/permix/src/vue/composables.test.ts
+++ b/permix/src/vue/composables.test.ts
@@ -56,6 +56,30 @@ describe('composables', () => {
expect(wrapper.get('[data-testid="read"]').text()).toBe('false')
})
+ it('reads ready state on the first render when setup ran before subscribe', () => {
+ const permix = createPermix<{
+ post: ['create']
+ }>()
+
+ permix.setup({
+ post: {
+ create: true,
+ },
+ })
+
+ const TestWrapper = defineComponent({
+ template: '{{ isReady }}:{{ check("post.create") }}
',
+ setup() {
+ const { check, isReady } = usePermix(permix)
+ return { check, isReady }
+ },
+ })
+
+ const wrapper = mountWithPermix(TestWrapper, permix)
+
+ expect(wrapper.get('div').text()).toBe('true:true')
+ })
+
it('should work with DOM rerender', async () => {
const permix = createPermix<{
post: [{ name: 'create'; type: { id: string } }, 'read']
From 3fddf3c398aa969d05c8ab7edd3a8a7bb15d2184 Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 13:30:34 +0200
Subject: [PATCH 05/28] fix: skip vendored agent skills in Oxfmt and Oxlint
Toolchain skills from skills.sh fail format check and are not project source.
Co-authored-by: Cursor
---
ignores.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/ignores.ts b/ignores.ts
index 9e77439e..8edf9972 100644
--- a/ignores.ts
+++ b/ignores.ts
@@ -9,6 +9,7 @@ export const ignorePatterns = [
'**/out/**',
'**/node_modules/**',
'**/_artifacts/**',
+ '**/.agents/**',
'**/next-env.d.ts',
'**/*.gen.ts',
'**/*.generated.ts',
From 42aaa5717728830d240991d762bd09b6c320e9c8 Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 13:32:10 +0200
Subject: [PATCH 06/28] fix: format React public API and allow React 18 peer
mismatches in CI
Oxfmt rejected the barrel and type-test files, and pinning React 18 in the catalog conflicts with docs/fumadocs React 19 peers.
Co-authored-by: Cursor
---
.github/workflows/react-compatibility.yml | 3 +-
permix/src/react/index.ts | 1 -
permix/test-d/public-api.ts | 48 +++++++++++------------
3 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/.github/workflows/react-compatibility.yml b/.github/workflows/react-compatibility.yml
index 7d7ebce3..e5bb4a70 100644
--- a/.github/workflows/react-compatibility.yml
+++ b/.github/workflows/react-compatibility.yml
@@ -48,7 +48,8 @@ jobs:
- name: Install pinned Permix dependencies
if: matrix.pin
- run: pnpm install --filter permix --no-frozen-lockfile
+ # Catalog pin is workspace-wide; docs/fumadocs still declare React 19 peers.
+ run: pnpm install --filter permix --no-frozen-lockfile --config.strictPeerDependencies=false
- name: Install dependencies
if: ${{ !matrix.pin }}
diff --git a/permix/src/react/index.ts b/permix/src/react/index.ts
index 3c1ae27e..d85c804e 100644
--- a/permix/src/react/index.ts
+++ b/permix/src/react/index.ts
@@ -4,4 +4,3 @@ export { createPermix } from './create-permix'
export type { CreatePermixResult } from './create-permix'
export { usePermix } from './hooks'
export type { PermixContext } from './hooks'
-
diff --git a/permix/test-d/public-api.ts b/permix/test-d/public-api.ts
index 19108ac1..a4079921 100644
--- a/permix/test-d/public-api.ts
+++ b/permix/test-d/public-api.ts
@@ -11,8 +11,8 @@ import { createPermix as createNextPermix } from 'permix/next'
import { createPermix as createNodePermix } from 'permix/node'
import { createPermix as createOrpcPermix } from 'permix/orpc'
import {
- createComponents as createReactComponents,
- createPermix as createReactPermix,
+ createComponents as createReactComponents,
+ createPermix as createReactPermix,
} from 'permix/react'
import { createPermix as createServerPermix } from 'permix/server'
import { createComponents as createSolidComponents } from 'permix/solid'
@@ -59,26 +59,26 @@ type FactoryCheck = ReturnType<(typeof reactFactory)['usePermix']>['check']
const factoryCheck: FactoryCheck = core.check
export {
- allowed,
- core,
- drizzle,
- drizzleLegacy,
- effect,
- elysia,
- express,
- factoryCheck,
- fastify,
- hono,
- next,
- node,
- orpc,
- react,
- reactFactory,
- reactStandalone,
- server,
- solid,
- svelte,
- tanstackStart,
- trpc,
- vue
+ allowed,
+ core,
+ drizzle,
+ drizzleLegacy,
+ effect,
+ elysia,
+ express,
+ factoryCheck,
+ fastify,
+ hono,
+ next,
+ node,
+ orpc,
+ react,
+ reactFactory,
+ reactStandalone,
+ server,
+ solid,
+ svelte,
+ tanstackStart,
+ trpc,
+ vue,
}
From d8519df72da05e68e6a1b5270f2c379914de098c Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 13:34:10 +0200
Subject: [PATCH 07/28] fix: drop extra blank line from public API type test
Oxfmt requires a single trailing newline after the merge from the React factory branch.
Co-authored-by: Cursor
---
permix/test-d/public-api.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/permix/test-d/public-api.ts b/permix/test-d/public-api.ts
index 9a3cb4ff..472ef0c1 100644
--- a/permix/test-d/public-api.ts
+++ b/permix/test-d/public-api.ts
@@ -87,4 +87,3 @@ export {
trpc,
vue,
}
-
From 8ec7576d5ceee047869fe6b9d001d2a9343ba539 Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 14:00:48 +0200
Subject: [PATCH 08/28] chore: add changelog, Release Please, and OSS
conventions
Reconstruct published npm history, show it on the docs site, and wire conventional-commit releases.
Co-authored-by: Cursor
---
.github/ISSUE_TEMPLATE/bug_report.yml | 94 ++
.github/ISSUE_TEMPLATE/config.yml | 5 +
.github/ISSUE_TEMPLATE/feature_request.yml | 41 +
.github/dependabot.yml | 28 +
.github/pull_request_template.md | 18 +
.github/release.yml | 22 +
.github/workflows/npm-publish.yml | 2 +
.github/workflows/release-please.yml | 27 +
.husky/commit-msg | 1 +
.release-please-manifest.json | 3 +
AGENTS.md | 5 +-
CHANGELOG.md | 984 ++++++++++++++++++
CODE_OF_CONDUCT.md | 79 ++
CONTRIBUTING.md | 64 ++
README.md | 8 +
SECURITY.md | 13 +
commitlint.config.mts | 5 +
docs/content/docs/changelog.mdx | 5 +
docs/content/docs/meta.json | 1 +
docs/package.json | 1 +
docs/remark-include-changelog.ts | 44 +
docs/source.config.ts | 4 +-
docs/src/lib/shared.ts | 8 +
docs/src/routes/docs/$.tsx | 4 +-
docs/vite.config.ts | 24 +
package.json | 6 +-
permix/skills/README.md | 2 +-
permix/skills/permix-getting-started/SKILL.md | 2 +-
permix/skills/permix/SKILL.md | 2 +-
pnpm-lock.yaml | 492 ++++++++-
release-please-config.json | 16 +
scripts/generate-historical-changelog.mjs | 216 ++++
scripts/historical-releases.json | 362 +++++++
scripts/tag-historical-releases.mjs | 63 ++
34 files changed, 2597 insertions(+), 54 deletions(-)
create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml
create mode 100644 .github/ISSUE_TEMPLATE/config.yml
create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml
create mode 100644 .github/dependabot.yml
create mode 100644 .github/pull_request_template.md
create mode 100644 .github/release.yml
create mode 100644 .github/workflows/release-please.yml
create mode 100644 .husky/commit-msg
create mode 100644 .release-please-manifest.json
create mode 100644 CHANGELOG.md
create mode 100644 CODE_OF_CONDUCT.md
create mode 100644 CONTRIBUTING.md
create mode 100644 SECURITY.md
create mode 100644 commitlint.config.mts
create mode 100644 docs/content/docs/changelog.mdx
create mode 100644 docs/remark-include-changelog.ts
create mode 100644 release-please-config.json
create mode 100644 scripts/generate-historical-changelog.mjs
create mode 100644 scripts/historical-releases.json
create mode 100644 scripts/tag-historical-releases.mjs
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 00000000..81dbe331
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,94 @@
+name: Bug report
+description: Something isn't working as expected
+title: '[Bug]: '
+labels: ['bug']
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Before opening a bug report, please check that the issue hasn't already been reported.
+
+ - type: textarea
+ id: description
+ attributes:
+ label: Describe the bug
+ description: A clear and concise description of what the bug is.
+ validations:
+ required: true
+
+ - type: textarea
+ id: reproduce
+ attributes:
+ label: Minimal reproduction
+ description: A minimal code snippet or repository that reproduces the issue.
+ placeholder: |
+ import { createPermix } from 'permix'
+
+ const permix = createPermix<{ post: ['read'] }>()
+ permix.setup({ post: { read: true } })
+ permix.check('post.read')
+ validations:
+ required: true
+
+ - type: textarea
+ id: expected
+ attributes:
+ label: Expected behavior
+ description: What did you expect to happen?
+ validations:
+ required: true
+
+ - type: textarea
+ id: actual
+ attributes:
+ label: Actual behavior
+ description: What actually happened? Include any error messages or console output.
+ validations:
+ required: true
+
+ - type: input
+ id: version
+ attributes:
+ label: permix version
+ placeholder: e.g. 4.1.2
+ validations:
+ required: true
+
+ - type: dropdown
+ id: framework
+ attributes:
+ label: Framework / adapter
+ options:
+ - Core (no adapter)
+ - React
+ - Vue
+ - Solid
+ - Svelte
+ - Next.js
+ - TanStack Start
+ - Express
+ - Hono
+ - Fastify
+ - tRPC
+ - oRPC
+ - Elysia
+ - Node
+ - Other
+ validations:
+ required: true
+
+ - type: input
+ id: framework_version
+ attributes:
+ label: Framework version
+ placeholder: e.g. React 19.2, Next.js 16
+ validations:
+ required: false
+
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: Checklist
+ options:
+ - label: I searched existing issues first
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000..203644b5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Documentation
+ url: https://permix.letstri.dev/docs
+ about: Usage questions are often answered in the docs.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 00000000..5a0fe5b6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,41 @@
+name: Feature request
+description: Propose a DX, API, or adapter improvement
+title: '[Feature]: '
+labels: ['enhancement']
+body:
+ - type: input
+ id: summary
+ attributes:
+ label: Summary
+ placeholder: Add a typed helper for checking several permissions at once
+ validations:
+ required: true
+
+ - type: textarea
+ id: problem
+ attributes:
+ label: Problem
+ description: What workflow or maintenance pain does this solve?
+ validations:
+ required: true
+
+ - type: textarea
+ id: proposal
+ attributes:
+ label: Proposed solution
+ description: Keep this focused on implementation shape, not product marketing.
+ validations:
+ required: true
+
+ - type: textarea
+ id: alternatives
+ attributes:
+ label: Alternatives considered
+
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: Checklist
+ options:
+ - label: I checked existing issues and docs first
+ required: true
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..489b5f19
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,28 @@
+version: 2
+
+updates:
+ - package-ecosystem: npm
+ directory: /
+ schedule:
+ interval: weekly
+ day: monday
+ time: '05:00'
+ timezone: UTC
+ open-pull-requests-limit: 10
+ groups:
+ workspace-dependencies:
+ patterns:
+ - '*'
+
+ - package-ecosystem: github-actions
+ directory: /
+ schedule:
+ interval: weekly
+ day: monday
+ time: '05:30'
+ timezone: UTC
+ open-pull-requests-limit: 5
+ groups:
+ github-actions:
+ patterns:
+ - '*'
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 00000000..51ac51a0
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,18 @@
+## Summary
+
+- [ ] closes #
+- [ ] follow-up issues opened and linked
+
+## Testing
+
+- [ ] `pnpm format:check`
+- [ ] `pnpm lint`
+- [ ] `pnpm check-types`
+- [ ] `pnpm test`
+- [ ] `pnpm verify`
+
+## Checklist
+
+- [ ] docs updated where needed (`docs/content/docs/`, `README.md`, `CONTRIBUTING.md`)
+- [ ] `permix/skills/` aligned when public API or integration patterns changed
+- [ ] added or updated tests where it materially reduces regression risk
diff --git a/.github/release.yml b/.github/release.yml
new file mode 100644
index 00000000..4eb61d04
--- /dev/null
+++ b/.github/release.yml
@@ -0,0 +1,22 @@
+changelog:
+ exclude:
+ labels:
+ - ignore-for-release
+ categories:
+ - title: Breaking Changes
+ labels:
+ - breaking
+ - title: New Features
+ labels:
+ - enhancement
+ - feature
+ - title: Bug Fixes
+ labels:
+ - bug
+ - fix
+ - title: Documentation
+ labels:
+ - documentation
+ - title: Other Changes
+ labels:
+ - '*'
diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml
index 47318b17..4bc2cf79 100644
--- a/.github/workflows/npm-publish.yml
+++ b/.github/workflows/npm-publish.yml
@@ -1,6 +1,8 @@
name: Build and publish
on:
+ release:
+ types: [published]
workflow_dispatch:
permissions:
diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml
new file mode 100644
index 00000000..e6251c62
--- /dev/null
+++ b/.github/workflows/release-please.yml
@@ -0,0 +1,27 @@
+name: Release Please
+
+on:
+ push:
+ branches:
+ - main
+
+permissions:
+ contents: write
+ issues: write
+ pull-requests: write
+
+concurrency:
+ group: release-please-${{ github.ref }}
+ cancel-in-progress: false
+
+jobs:
+ release-please:
+ name: Release Please
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - name: Run Release Please
+ uses: googleapis/release-please-action@v5
+ with:
+ config-file: release-please-config.json
+ manifest-file: .release-please-manifest.json
diff --git a/.husky/commit-msg b/.husky/commit-msg
new file mode 100644
index 00000000..9ef41ae4
--- /dev/null
+++ b/.husky/commit-msg
@@ -0,0 +1 @@
+pnpm commitlint --edit "$1"
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
new file mode 100644
index 00000000..be491988
--- /dev/null
+++ b/.release-please-manifest.json
@@ -0,0 +1,3 @@
+{
+ "permix": "4.1.2"
+}
diff --git a/AGENTS.md b/AGENTS.md
index bde723ed..3f8daca1 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -8,7 +8,7 @@ Skills for teams **using** Permix ship in the published npm package at [`permix/
Repo-root [`_artifacts/`](_artifacts/skill_tree.yaml) (`domain_map.yaml`, `skill_spec.md`, `skill_tree.yaml`) tracks skill coverage and source-doc references for CI staleness checks.
-When you change public API behavior, docs examples, or integration patterns, keep `permix/skills/` aligned with `docs/content/docs/` and `examples/`, bump `library_version` in SKILL frontmatter on release, and run `cd permix && pnpm run skills:stale`.
+When you change public API behavior, docs examples, or integration patterns, keep `permix/skills/` aligned with `docs/content/docs/` and `examples/`, then run `cd permix && pnpm run skills:stale`. Release Please bumps `library_version` in SKILL frontmatter on release.
## Repository layout
@@ -33,6 +33,7 @@ From repo root (pnpm workspace: `permix`, `docs`, `examples/*`):
```bash
pnpm install
+pnpm verify # format, lint, test, types, build
pnpm test && pnpm run check-types
pnpm run lint
pnpm run format
@@ -42,4 +43,6 @@ cd docs && pnpm dev # http://localhost:3000
cd docs && pnpm types:check # fumadocs-mdx + tsc for docs only
```
+Use [Conventional Commits](https://www.conventionalcommits.org/). Husky runs commitlint on `commit-msg`. Release Please on `main` opens a release PR that bumps `permix`, updates [CHANGELOG.md](CHANGELOG.md), and tags `vMAJOR.MINOR.PATCH`. Merging that PR publishes a GitHub Release; npm publish runs from `.github/workflows/npm-publish.yml`. The docs changelog page (`/docs/changelog`) inlines repo-root `CHANGELOG.md` at compile time.
+
Do not commit unless the user asks.
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..a2916b1d
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,984 @@
+# Changelog
+
+All notable changes to `permix` are documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Entries through 4.1.2 were reconstructed from npm publish dates and git history; later versions are maintained by Release Please.
+
+## [4.1.2](https://github.com/letstri/permix/compare/v4.1.1...v4.1.2) (2026-07-02)
+
+### Bug Fixes
+
+- fix: tanstack start docs
+
+### Documentation
+
+- docs: improve for agents
+- docs: fix llms link
+
+### Miscellaneous
+
+- refactor: update skills
+- chore: add claude launch
+- updates
+- add tanstack/intent
+
+## [4.1.1](https://github.com/letstri/permix/compare/v4.1.0...v4.1.1) (2026-06-11)
+
+### Miscellaneous
+
+- update skills
+- fixes
+- updates
+
+## [4.1.0](https://github.com/letstri/permix/compare/v4.0.1...v4.1.0) (2026-06-08)
+
+### Bug Fixes
+
+- fix: change jsx in tsconfig to react-jsx
+
+### Miscellaneous
+
+- fix trpc types
+- add tanstack start example
+- refactor: change symbol to plain string
+- update docs
+- add new 'check' hook and add hooks to each integration
+- updates
+
+## [4.0.1](https://github.com/letstri/permix/compare/v4.0.0...v4.0.1) (2026-06-03)
+
+### Miscellaneous
+
+- updates
+- updates
+- updates
+- updates
+- updates
+- add workflows
+- update docs
+- minor
+- updates
+- remove better auth
+- update comments
+
+## [4.0.0](https://github.com/letstri/permix/compare/v3.8.1...v4.0.0) (2026-06-03)
+
+### Miscellaneous
+
+- update packages
+- updates
+- updates
+- fixes
+- fixes
+- updates
+- add svelte
+- rename typeRequired to required
+- fix doc
+- updates
+- add better auth
+- remove commitlint
+- chore: add drizzle
+- v4
+- chore: add zed config
+- chore: add imports
+
+## [3.8.1](https://github.com/letstri/permix/compare/v3.8.0...v3.8.1) (2026-04-29)
+
+### Bug Fixes
+
+- fix(elysia): relax checkHandler context type to support schema validation
+
+## [3.8.0](https://github.com/letstri/permix/compare/v3.7.0...v3.8.0) (2026-03-23)
+
+### Features
+
+- feat(better-auth): export PermixSession interface
+
+### Miscellaneous
+
+- feat(better-auth)!: redesign API — split permixPlugin, createPermix, permixClient
+- refactor: update publish workflow
+
+## [3.7.0](https://github.com/letstri/permix/compare/v3.6.0...v3.7.0) (2026-03-19)
+
+### Features
+
+- feat(better-auth): add server and client plugins for Better Auth integration
+- feat(orpc): allow customizing the context key for the permix instance
+
+### Documentation
+
+- docs(better-auth): add integration docs page
+- docs: fix permix check function
+
+### Miscellaneous
+
+- refactor: improve better auth types
+- chore(better-auth): add build entry, subpath export, and peer dependency
+- test(better-auth): add tests for Better Auth plugin
+- chore: update packages
+- chore: change fumadocs theme
+- chore: update packages
+- refactor: move all packages to workspace
+- chore: remove context7
+- chore: add context7
+
+## [3.6.0](https://github.com/letstri/permix/compare/v3.5.2...v3.6.0) (2025-11-06)
+
+### Features
+
+- feat: add "any" keyword for permissions checking
+
+## [3.5.2](https://github.com/letstri/permix/compare/v3.5.1...v3.5.2) (2025-11-04)
+
+### Bug Fixes
+
+- fix: add react-dom ad optional peer
+- fix: markdown url 404 by proxying to github raw files
+
+### Documentation
+
+- docs: fix build
+- docs: update packages
+- docs: update fumadocs
+- docs: restore twoslash
+- docs: fix llms
+- docs(refactor): added new toc style in docs
+
+### Miscellaneous
+
+- chore: update packages
+
+## [3.5.1](https://github.com/letstri/permix/compare/v3.5.0...v3.5.1) (2025-11-02)
+
+### Miscellaneous
+
+- chore: update packages
+- chore: update packages
+- refactor: convert script from js to ts
+- chore: bump node version
+- refactor: add template to trpc
+- refactor: add more tests
+- chore: update packages
+
+## [3.5.0](https://github.com/letstri/permix/compare/v3.4.1...v3.5.0) (2025-08-02)
+
+### Features
+
+- feat: add dehydrate and hydrate to permix instance
+
+### Documentation
+
+- docs: update hydration to new api
+
+## [3.4.1](https://github.com/letstri/permix/compare/v3.4.0...v3.4.1) (2025-07-23)
+
+### Bug Fixes
+
+- fix: add typescript export to avoid missing types
+
+### Documentation
+
+- docs: improve llms
+- docs: add llms-full
+- docs: fix example
+- docs: rename page
+
+### Miscellaneous
+
+- chore: update packages
+- refactor: simplify setup
+- chore: update lock
+- chore: add more test for hydration
+
+## [3.4.0](https://github.com/letstri/permix/compare/v3.3.0...v3.4.0) (2025-07-09)
+
+### Features
+
+- feat: add `template` function to backend integrations
+
+### Documentation
+
+- docs: update intro
+- docs: fix solid example
+
+### Miscellaneous
+
+- chore: update packages
+- refactor: remove `checkAsync` method from the backend integrations
+- chore: update lock
+- chore: clean dependencies
+
+## [3.3.0](https://github.com/letstri/permix/compare/v3.2.1...v3.3.0) (2025-06-24)
+
+### Features
+
+- feat: add Solid.js integration
+
+### Documentation
+
+- docs: add Solid.js example
+- docs: add Solid.js integration
+- docs: add `dataRequired` and fix styles
+
+### Miscellaneous
+
+- chore: update packages
+- build: use Babel insted of esbuild
+- chore: update packages
+
+## [3.2.1](https://github.com/letstri/permix/compare/v3.2.0...v3.2.1) (2025-06-23)
+
+### Documentation
+
+- docs: improve docs to new api
+
+### Miscellaneous
+
+- refactor: update exported types
+
+## [3.2.0](https://github.com/letstri/permix/compare/v3.1.0...v3.2.0) (2025-06-22)
+
+### Features
+
+- feat: add dataRequired prop and update datType type
+
+### Miscellaneous
+
+- chore: remove useless code, add console error
+- refactor: improved javascript error handling
+
+## [3.1.0](https://github.com/letstri/permix/compare/v3.0.0...v3.1.0) (2025-06-22)
+
+### Features
+
+- feat: add Fastify integration
+
+### Documentation
+
+- docs: improve some parts
+- docs: add Fastify integration
+- docs: update orpc
+- docs: add initial section
+- docs: update orpc section
+
+### Miscellaneous
+
+- chore: fix lock
+- refactor: update examples
+- refactor: rename setState method
+- build: add Fastify integration
+- chore: update lock
+- chore: update permix version
+- chore: add external package
+
+## [3.0.0](https://github.com/letstri/permix/compare/v2.1.5...v3.0.0) (2025-06-21)
+
+### Features
+
+- feat: add elysia integration and packages updates
+
+### Documentation
+
+- docs: update setup due to refactor
+- docs: add elysia
+- docs: fix build
+- docs: fix build
+
+### Miscellaneous
+
+- refactor: update trpc, orpc, vue integrations
+- chore: add funding
+- chore: update packages
+- chore: add funding
+- chore: add more tests
+
+## [2.1.5](https://github.com/letstri/permix/compare/v2.1.4...v2.1.5) (2025-04-09)
+
+### Bug Fixes
+
+- fix: add exports to utils
+
+### Documentation
+
+- docs: add orpc
+
+## [2.1.4](https://github.com/letstri/permix/compare/v2.1.3...v2.1.4) (2025-04-08)
+
+### Bug Fixes
+
+- fix: add orpc export
+
+## [2.1.3](https://github.com/letstri/permix/compare/v2.1.2...v2.1.3) (2025-04-08)
+
+### Bug Fixes
+
+- fix: add orpc to export
+
+## [2.1.2](https://github.com/letstri/permix/compare/v2.1.1...v2.1.2) (2025-04-08)
+
+### Bug Fixes
+
+- fix: orpc context
+
+## [2.1.1](https://github.com/letstri/permix/compare/v2.1.0...v2.1.1) (2025-04-08)
+
+### Bug Fixes
+
+- fix: trpc context
+
+## [2.1.0](https://github.com/letstri/permix/compare/v2.0.0...v2.1.0) (2025-04-08)
+
+### Features
+
+- feat: add orpc integration
+
+### Bug Fixes
+
+- fix: instance docs
+
+### Documentation
+
+- docs: update node and express
+- docs: minor changes
+- docs: minor
+- docs: improve landing
+- docs: fix type
+- docs: update instance
+- docs: add initial
+
+### Miscellaneous
+
+- refactor: trpc plugin to use initTRPC
+- chore: update packages
+
+## [2.0.0](https://github.com/letstri/permix/compare/v2.0.0-rc.13...v2.0.0) (2025-03-06)
+
+### Features
+
+- feat: add test for initial state
+- feat: add initial state for permix
+
+### Bug Fixes
+
+- fix: test for initial state
+
+### Miscellaneous
+
+- chore: update packages
+- refactor: remove server
+- refactor: remove server
+
+## [2.0.0-rc.13](https://github.com/letstri/permix/compare/v2.0.0-rc.12...v2.0.0-rc.13) (2025-03-03)
+
+### Bug Fixes
+
+- fix: export types
+
+## [2.0.0-rc.12](https://github.com/letstri/permix/compare/v2.0.0-rc.11...v2.0.0-rc.12) (2025-03-03)
+
+### Bug Fixes
+
+- fix: types
+
+### Documentation
+
+- docs: add get rules
+
+### Miscellaneous
+
+- chore: update versions
+
+## [2.0.0-rc.11](https://github.com/letstri/permix/compare/v2.0.0-rc.10...v2.0.0-rc.11) (2025-03-03)
+
+### Miscellaneous
+
+- refactor: rename method
+
+## [2.0.0-rc.10](https://github.com/letstri/permix/compare/v2.0.0-rc.9...v2.0.0-rc.10) (2025-03-03)
+
+### Documentation
+
+- docs: fix templates
+
+### Miscellaneous
+
+- refactor: improve state
+
+## [2.0.0-rc.9](https://github.com/letstri/permix/compare/v2.0.0-rc.8...v2.0.0-rc.9) (2025-03-03)
+
+### Documentation
+
+- docs: minor
+- docs: update description
+- docs: add link
+
+### Miscellaneous
+
+- refactor: templates
+- refactor: internals
+- chore: update packages
+
+## [2.0.0-rc.8](https://github.com/letstri/permix/compare/v2.0.0-rc.7...v2.0.0-rc.8) (2025-02-28)
+
+### Documentation
+
+- docs: update
+
+### Miscellaneous
+
+- refactor: backend internals
+
+## [2.0.0-rc.7](https://github.com/letstri/permix/compare/v2.0.0-rc.6...v2.0.0-rc.7) (2025-02-28)
+
+### Miscellaneous
+
+- refactor: backend adapters
+- chore: improve tests, remove coverage
+- refactor: improve server handlers
+- refactor: improve server handlers
+- Revert "refactor: internal server"
+- Revert "refactor: minor"
+- Revert "refactor: minor"
+- refactor: minor
+- refactor: minor
+- refactor: internal server
+
+## [2.0.0-rc.6](https://github.com/letstri/permix/compare/v2.0.0-rc.5...v2.0.0-rc.6) (2025-02-27)
+
+### Bug Fixes
+
+- fix: docs
+
+### Documentation
+
+- docs: update version
+
+### Miscellaneous
+
+- refactor: rename functions
+
+## [2.0.0-rc.5](https://github.com/letstri/permix/compare/v2.0.0-rc.4...v2.0.0-rc.5) (2025-02-27)
+
+### Documentation
+
+- docs: update version
+
+### Miscellaneous
+
+- refactor: remove res
+
+## [2.0.0-rc.4](https://github.com/letstri/permix/compare/v2.0.0-rc.3...v2.0.0-rc.4) (2025-02-27)
+
+### Features
+
+- feat: add node and server
+
+### Documentation
+
+- docs: minor
+- docs: update package
+
+### Miscellaneous
+
+- refactor: backend integrations
+
+## [2.0.0-rc.3](https://github.com/letstri/permix/compare/v2.0.0-rc.2...v2.0.0-rc.3) (2025-02-27)
+
+### Documentation
+
+- docs: update version
+
+### Miscellaneous
+
+- refactor: templates
+- refactor: docs
+
+## [2.0.0-rc.2](https://github.com/letstri/permix/compare/v2.0.0-rc.1...v2.0.0-rc.2) (2025-02-27)
+
+### Miscellaneous
+
+- refactor: packages
+- refactor: templates
+
+## [2.0.0-rc.1](https://github.com/letstri/permix/compare/v2.0.0-beta.1...v2.0.0-rc.1) (2025-02-26)
+
+### Bug Fixes
+
+- fix: workflow
+- fix: workflow
+- fix: workflow
+
+### Documentation
+
+- docs: update package
+
+### Miscellaneous
+
+- refactor: templates
+- chore: update package
+- chore: update packages
+
+## [2.0.0-beta.1](https://github.com/letstri/permix/compare/v1.0.4...v2.0.0-beta.1) (2025-02-26)
+
+### Bug Fixes
+
+- fix: trpc middleware types
+- fix: express example
+
+### Documentation
+
+- docs: update express, hono, trpc based on new features
+
+### Miscellaneous
+
+- refactor: hono middleware
+- chore: fix lock
+- refactor: internal instances
+- refactor: remove next and nuxt
+- refactor: remove nuxt
+- refactor: remove next and nuxt
+- refactor: trpc middleware
+- refactor: rename express methods
+- refactor: express minor
+- refactor: rename express instance
+- refactor: finish express middlewares
+- refactor: minor updates
+- refactor: minor updates
+- refactor: update template, refactor express middleware
+- refactor: update types
+- refactor: update types
+- chore: improve examples
+
+## [1.0.4](https://github.com/letstri/permix/compare/v1.0.3...v1.0.4) (2025-02-04)
+
+### Bug Fixes
+
+- fix: vue prop type
+
+### Documentation
+
+- docs: fix nuxt
+- docs: fix nuxt
+
+## [1.0.3](https://github.com/letstri/permix/compare/v1.0.2...v1.0.3) (2025-02-03)
+
+### Documentation
+
+- docs: add known issues
+
+### Miscellaneous
+
+- refactor: internal interfaces
+- chore: update coverage
+- chore: add build
+
+## [1.0.2](https://github.com/letstri/permix/compare/v1.0.1...v1.0.2) (2025-02-02)
+
+### Bug Fixes
+
+- fix: remove useless prop
+
+## [1.0.1](https://github.com/letstri/permix/compare/v1.0.0...v1.0.1) (2025-02-02)
+
+### Features
+
+- feat: add new interface to use as components definition
+
+### Bug Fixes
+
+- fix: lock
+
+### Documentation
+
+- docs: fix vue
+- docs: update instance
+- docs: update instance
+- docs: fix names
+- docs: update introduction
+- docs: update introduction
+- docs: update introduction
+- docs: update introduction
+- docs: improve introduction
+- docs: change permix version
+- docs: change permix to workspace
+- docs: fix link
+- docs: fix typo
+
+### Miscellaneous
+
+- chore: ignore test for nuxt
+- refactor: rename commitlint config
+- refactor: update spaces
+
+## [1.0.0](https://github.com/letstri/permix/compare/v1.0.0-rc.2...v1.0.0) (2025-01-22)
+
+### Features
+
+- feat: add to react and vue check component `otherwise`
+
+### Bug Fixes
+
+- fix: react context updates
+- fix: trpc middleware types
+- fix: sandbox
+- fix: sandbox
+- fix: react component generic
+
+### Documentation
+
+- docs: update examples
+- docs: minor improvements
+- docs: remove useless
+- docs: improvements
+- docs: minor improvements
+- docs: add compare, feature flags, some imrovements
+- docs: update integrations
+
+### Miscellaneous
+
+- chore: update lock
+- refactor: remove infers
+- refactor: use method to check isready
+- chore: improve vue tests
+- refactor: improve build
+- chore: add more examples
+- refactor: remove useless type
+- refactor: tests
+- chore: update examples
+- chore: add more examples
+- chore: minor improvements
+- refactor: internal serialization
+- chore: update example
+- refactor: slots
+- chore: update examples
+- refactor: vue component
+
+## [1.0.0-rc.2](https://github.com/letstri/permix/compare/v1.0.0-rc.1...v1.0.0-rc.2) (2025-01-22)
+
+### Features
+
+- feat: add vue check component
+- feat: add react component
+
+### Miscellaneous
+
+- chore: update coverage
+- chore: add more examples
+- chore: update packages
+
+## [1.0.0-rc.1](https://github.com/letstri/permix/compare/v0.8.0...v1.0.0-rc.1) (2025-01-22)
+
+### Features
+
+- feat: add isReadyAsync
+- feat: add nuxt
+
+### Documentation
+
+- docs: improvements
+- docs: add nuxt integration
+- docs: improvements
+- docs: add files to react
+
+## [0.8.0](https://github.com/letstri/permix/compare/v0.7.3...v0.8.0) (2025-01-21)
+
+### Features
+
+- feat: improve hydration
+
+### Bug Fixes
+
+- fix: lock
+
+### Documentation
+
+- docs: add nextjs
+- docs: add hydration
+- docs: update enums
+- docs: improve docs
+- docs: add analytics
+- docs: add analytics
+- docs: add examples
+- docs: update version
+- docs: update version
+- docs: add integrations
+
+### Miscellaneous
+
+- refactor: internals
+- refactor: vue composable
+- refactor: coverage
+- refactor: remove async from setup, add custom hooks, update tests
+- refactor: internals
+- refactor: internals
+- chore: update next example
+- refactor: update internals
+- chore: update lock
+- refactor: react internal
+- chore: updates
+- chore: add next example
+- refactor: internal checks
+- refactor: add links
+
+## [0.7.3](https://github.com/letstri/permix/compare/v0.7.2...v0.7.3) (2025-01-18)
+
+### Documentation
+
+- docs: finish main section
+
+### Miscellaneous
+
+- refactor: add links, update hooks
+
+## [0.7.2](https://github.com/letstri/permix/compare/v0.7.2-beta.2...v0.7.2) (2025-01-18)
+
+### Bug Fixes
+
+- fix: publish command
+- fix: publish command
+- fix: publish command
+
+## [0.7.2-beta.2](https://github.com/letstri/permix/compare/v0.7.2-beta.1...v0.7.2-beta.2) (2025-01-18)
+
+- Published to npm.
+
+## [0.7.2-beta.1](https://github.com/letstri/permix/compare/v0.7.1...v0.7.2-beta.1) (2025-01-18)
+
+### Bug Fixes
+
+- fix: publish command
+
+### Miscellaneous
+
+- refactor: remove readme
+
+## [0.7.1](https://github.com/letstri/permix/compare/v0.7.0...v0.7.1) (2025-01-18)
+
+### Features
+
+- feat: add auto-publish
+
+### Bug Fixes
+
+- fix: publish command
+- fix: publish command
+- fix: publish command
+- fix: publish command
+
+### Documentation
+
+- docs: remove useless file
+- docs: add quick start
+
+### Miscellaneous
+
+- refactor: add scripts and coverage
+- chore: change docs link
+- chore: change docs link
+
+## [0.7.0](https://github.com/letstri/permix/compare/v0.6.0...v0.7.0) (2025-01-17)
+
+### Features
+
+- feat: add async check
+
+### Bug Fixes
+
+- fix: temp remove setup from options
+
+### Miscellaneous
+
+- refactor: rename type
+
+## [0.6.0](https://github.com/letstri/permix/compare/v0.5.0...v0.6.0) (2025-01-17)
+
+### Features
+
+- feat: improve error handling
+
+### Miscellaneous
+
+- chore: add link to docs
+
+## [0.5.0](https://github.com/letstri/permix/compare/v0.4.2...v0.5.0) (2025-01-17)
+
+### Features
+
+- feat: add to react and vue `isReady`
+
+### Miscellaneous
+
+- chore: add examples
+
+## [0.4.2](https://github.com/letstri/permix/compare/v0.4.1...v0.4.2) (2025-01-17)
+
+### Bug Fixes
+
+- fix: trpc types context
+
+### Miscellaneous
+
+- chore: update package
+
+## [0.4.1](https://github.com/letstri/permix/compare/v0.4.0...v0.4.1) (2025-01-17)
+
+### Documentation
+
+- docs: add introduction
+
+### Miscellaneous
+
+- refactor: remove initialPermissions
+
+## [0.4.0](https://github.com/letstri/permix/compare/v0.3.5...v0.4.0) (2025-01-16)
+
+### Features
+
+- feat: add param to template
+
+### Bug Fixes
+
+- fix: script
+
+### Miscellaneous
+
+- chore: update lock
+- chore: update packages
+
+## [0.3.5](https://github.com/letstri/permix/compare/v0.3.4...v0.3.5) (2025-01-16)
+
+### Bug Fixes
+
+- fix: backend types
+
+## [0.3.4](https://github.com/letstri/permix/compare/v0.3.3...v0.3.4) (2025-01-16)
+
+### Features
+
+- feat: add permissions definition
+
+## [0.3.3](https://github.com/letstri/permix/compare/v0.3.2...v0.3.3) (2025-01-16)
+
+### Bug Fixes
+
+- fix: react and vue `check` methods
+
+### Miscellaneous
+
+- refactor: improve internal methods
+
+## [0.3.2](https://github.com/letstri/permix/compare/v0.3.1...v0.3.2) (2025-01-16)
+
+### Bug Fixes
+
+- fix: reexport in react and vue
+
+### Miscellaneous
+
+- chore: update descriptions
+- refactor: remove useless directive
+
+## [0.3.1](https://github.com/letstri/permix/compare/v0.3.0...v0.3.1) (2025-01-15)
+
+### Bug Fixes
+
+- fix: datatype
+
+## [0.3.0](https://github.com/letstri/permix/compare/v0.2.1...v0.3.0) (2025-01-15)
+
+### Miscellaneous
+
+- refactor: make all permissions required, fix vue and react adapters
+- refactor: temp remove examples
+
+## [0.2.1](https://github.com/letstri/permix/compare/v0.2.0...v0.2.1) (2025-01-15)
+
+### Bug Fixes
+
+- fix: type in frontend
+- fix: readme
+
+### Documentation
+
+- docs: update frontend
+- docs: update backend
+- docs: update backend
+- docs: update backend
+
+### Miscellaneous
+
+- chore: remove useless command
+
+## [0.2.0](https://github.com/letstri/permix/compare/v0.1.2...v0.2.0) (2025-01-15)
+
+### Features
+
+- feat: add `all` permission
+
+### Documentation
+
+- docs: remove static
+- docs: temp comment links
+- docs: update landing
+
+### Miscellaneous
+
+- refactor: improve prepublish command
+
+## [0.1.2](https://github.com/letstri/permix/compare/v0.1.1...v0.1.2) (2025-01-15)
+
+### Bug Fixes
+
+- fix: text examples
+
+### Miscellaneous
+
+- chore: add some text
+- refactor: add private
+
+## [0.1.1](https://github.com/letstri/permix/compare/v0.1.0...v0.1.1) (2025-01-15)
+
+### Miscellaneous
+
+- refactor: update descriptions
+- refactor: remove turbo
+
+## [0.1.0](https://github.com/letstri/permix/compare/v0.0.1...v0.1.0) (2025-01-15)
+
+### Features
+
+- feat: add nuxt adapter
+- feat: add next and nuxt examples
+- feat: add hono middleware
+- feat: add express middleware
+
+### Bug Fixes
+
+- fix: build
+- fix: react and vue reactivity
+
+### Documentation
+
+- docs: init
+
+### Miscellaneous
+
+- refactor: update tests
+- chore: remove useless variable
+- chore: improve tests
+- refactor: add jsdoc
+
+## [0.0.1](https://github.com/letstri/permix/compare/v0.0.1-alpha.1...v0.0.1) (2025-01-14)
+
+### Miscellaneous
+
+- refactor: add workspaces, commitlint, turbo, some permix stuff
+- update readme
+- Create README.md
+- init first version
+- Initial commit
+
+## [0.0.1-alpha.1](https://github.com/letstri/permix/releases/tag/v0.0.1-alpha.1) (2025-01-13)
+
+- Published to npm.
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..269566fb
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,79 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our community include:
+
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the overall community
+
+Examples of unacceptable behavior include:
+
+- The use of sexualized language or imagery, and sexual attention or advances of any kind
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email address, without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at valerii.strilets@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/inclusion).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..1d9fce4d
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,64 @@
+# Contributing
+
+Thanks for helping improve Permix.
+
+## Prerequisites
+
+- Node `>=22` and pnpm `>=11` (`packageManager` in root `package.json`)
+- Run `pnpm install` from the repository root
+
+## Development workflow
+
+```bash
+pnpm format
+pnpm lint
+pnpm test
+pnpm check-types
+pnpm verify
+```
+
+`pnpm verify` is the closest local equivalent of the main CI quality gate (format, lint, tests, types, and the library build).
+
+Use focused branches. Commit messages must follow [Conventional Commits](https://www.conventionalcommits.org/):
+
+```
+feat: add createSetupHandler for TanStack Start
+fix(react): re-run client setup after re-hydration
+docs: document Permix checks in beforeLoad
+chore: bump oxlint
+```
+
+Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`. Breaking changes use `feat!:` / `fix!:` or a `BREAKING CHANGE:` footer. Husky runs commitlint on `commit-msg`.
+
+## Change expectations
+
+- Add or adjust tests when behavior changes in a meaningful way.
+- Keep `permix/skills/` aligned with `docs/content/docs/` and `examples/` when public API, docs examples, or integration patterns change.
+- Do not bump `permix` version or edit released changelog sections by hand. Release Please opens a release PR from conventional commits.
+
+## Documentation
+
+API documentation belongs in `docs/content/docs`. The [changelog page](https://permix.letstri.dev/docs/changelog) reads repo-root `CHANGELOG.md`; do not duplicate release notes in MDX.
+
+```bash
+cd docs && pnpm dev # http://localhost:3000
+```
+
+## Pull requests
+
+- Fill out the PR template.
+- Link related issues.
+- Include screenshots or recordings for docs-site UI changes.
+
+## Releases
+
+Releases are driven by Release Please on `main`. Merging the release PR creates a `vMAJOR.MINOR.PATCH` GitHub Release; the provenance-enabled publish workflow then publishes `permix` to npm. After a release, CI may open a skills review PR.
+
+Historical npm versions that predate this process can be tagged locally with:
+
+```bash
+node scripts/tag-historical-releases.mjs # dry run
+node scripts/tag-historical-releases.mjs --apply # create annotated tags
+```
+
+Do not bulk-create GitHub Releases for those tags.
diff --git a/README.md b/README.md
index 7b38be33..c17f4de2 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,14 @@ pnpm dlx @tanstack/intent@latest install
Skills are indexed on the [Agent Skills Registry](https://tanstack.com/intent/registry) and update when you update the package.
+## Changelog
+
+Release notes are in [CHANGELOG.md](CHANGELOG.md) and on the docs site at [permix.letstri.dev/docs/changelog](https://permix.letstri.dev/docs/changelog).
+
+## Contributing
+
+See [CONTRIBUTING.md](CONTRIBUTING.md). Please follow the [Code of Conduct](CODE_OF_CONDUCT.md). To report a vulnerability, see [SECURITY.md](SECURITY.md).
+
## License
MIT License - see the [LICENSE](https://github.com/letstri/permix/blob/main/LICENSE) file for details
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..c9416973
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,13 @@
+# Security Policy
+
+## Supported versions
+
+Security fixes are applied to the latest `permix` release on npm. Please upgrade before reporting issues that are already fixed.
+
+## Reporting a vulnerability
+
+Do not open a public GitHub issue for security reports.
+
+Report vulnerabilities through [GitHub private security advisories](https://github.com/letstri/permix/security/advisories/new). We will acknowledge the report, investigate, and coordinate a fix and disclosure.
+
+If GitHub advisories are unavailable, email [valerii.strilets@gmail.com](mailto:valerii.strilets@gmail.com).
diff --git a/commitlint.config.mts b/commitlint.config.mts
new file mode 100644
index 00000000..4e03c10d
--- /dev/null
+++ b/commitlint.config.mts
@@ -0,0 +1,5 @@
+const config = {
+ extends: ['@commitlint/config-conventional'],
+}
+
+export default config
diff --git a/docs/content/docs/changelog.mdx b/docs/content/docs/changelog.mdx
new file mode 100644
index 00000000..571634ff
--- /dev/null
+++ b/docs/content/docs/changelog.mdx
@@ -0,0 +1,5 @@
+---
+title: Changelog
+description: Release notes for the permix npm package
+icon: RiHistoryLine
+---
diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json
index dbd79bfc..1b584cbd 100644
--- a/docs/content/docs/meta.json
+++ b/docs/content/docs/meta.json
@@ -5,6 +5,7 @@
"quick-start",
"migration-v3-to-v4",
"comparison",
+ "changelog",
"---Guide---",
"guide/instance",
"guide/setup",
diff --git a/docs/package.json b/docs/package.json
index 7b7c16f4..d3e13bd5 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -24,6 +24,7 @@
"fumadocs-mdx": "^15.0.10",
"fumadocs-twoslash": "^3.2.0",
"fumadocs-ui": "^16.9.3",
+ "mdast-util-from-markdown": "^2.0.2",
"mermaid": "^11.15.0",
"permix": "workspace:*",
"react": "^19.2.6",
diff --git a/docs/remark-include-changelog.ts b/docs/remark-include-changelog.ts
new file mode 100644
index 00000000..2c134e8d
--- /dev/null
+++ b/docs/remark-include-changelog.ts
@@ -0,0 +1,44 @@
+import { readFileSync } from 'node:fs'
+import path from 'node:path'
+
+import { fromMarkdown } from 'mdast-util-from-markdown'
+
+interface MdastNode {
+ type: string
+ depth?: number
+ value?: string
+ children?: MdastNode[]
+}
+
+const changelogPath = path.resolve(import.meta.dirname, '..', 'CHANGELOG.md')
+
+function headingText(node: MdastNode): string {
+ return (node.children ?? [])
+ .map((child) => child.value ?? '')
+ .join('')
+ .trim()
+}
+
+export function remarkIncludeChangelog() {
+ return (tree: { children: MdastNode[] }, file: { path?: string }) => {
+ const filePath = file.path ?? ''
+ if (!filePath.endsWith('changelog.mdx')) {
+ return
+ }
+
+ const parsed = fromMarkdown(readFileSync(changelogPath, 'utf-8')) as {
+ children: MdastNode[]
+ }
+ const first = parsed.children[0]
+ if (
+ first &&
+ first.type === 'heading' &&
+ first.depth === 1 &&
+ headingText(first).toLowerCase() === 'changelog'
+ ) {
+ parsed.children.shift()
+ }
+
+ tree.children.push(...parsed.children)
+ }
+}
diff --git a/docs/source.config.ts b/docs/source.config.ts
index 71c2d569..d87f6c62 100644
--- a/docs/source.config.ts
+++ b/docs/source.config.ts
@@ -6,6 +6,8 @@ import { defineConfig, defineDocs } from 'fumadocs-mdx/config'
import { transformerTwoslash } from 'fumadocs-twoslash'
import { createFileSystemTypesCache } from 'fumadocs-twoslash/cache-fs'
+import { remarkIncludeChangelog } from './remark-include-changelog'
+
export const docs = defineDocs({
dir: 'content/docs',
docs: {
@@ -17,7 +19,7 @@ export const docs = defineDocs({
export default defineConfig({
mdxOptions: {
- remarkPlugins: [remarkMdxMermaid],
+ remarkPlugins: [remarkMdxMermaid, remarkIncludeChangelog],
rehypeCodeOptions: {
themes: {
light: 'github-light',
diff --git a/docs/src/lib/shared.ts b/docs/src/lib/shared.ts
index f8f21b35..92a881bf 100644
--- a/docs/src/lib/shared.ts
+++ b/docs/src/lib/shared.ts
@@ -7,3 +7,11 @@ export const gitConfig = {
repo: 'permix',
branch: 'main',
}
+
+export function docsGithubUrl(path: string) {
+ const base = `https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}`
+ if (path === 'changelog.mdx' || path === 'changelog.md') {
+ return `${base}/CHANGELOG.md`
+ }
+ return `${base}/docs/content/docs/${path}`
+}
diff --git a/docs/src/routes/docs/$.tsx b/docs/src/routes/docs/$.tsx
index d9d5649e..135f8c88 100644
--- a/docs/src/routes/docs/$.tsx
+++ b/docs/src/routes/docs/$.tsx
@@ -16,7 +16,7 @@ import { Suspense } from 'react'
import { useMDXComponents } from '@/components/mdx'
import { SidebarScrollFix } from '@/components/sidebar-scroll'
import { baseOptions } from '@/lib/layout.shared'
-import { gitConfig } from '@/lib/shared'
+import { docsGithubUrl } from '@/lib/shared'
import { slugsToMarkdownPath, source } from '@/lib/source'
const serverLoader = createServerFn({
@@ -65,7 +65,7 @@ const clientLoader = browserCollections.docs.createClientLoader({
diff --git a/docs/vite.config.ts b/docs/vite.config.ts
index 8005021a..bd5c0a47 100644
--- a/docs/vite.config.ts
+++ b/docs/vite.config.ts
@@ -1,3 +1,5 @@
+import path from 'node:path'
+
import tailwindcss from '@tailwindcss/vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import react from '@vitejs/plugin-react'
@@ -5,11 +7,33 @@ import mdx from 'fumadocs-mdx/vite'
import { nitro } from 'nitro/vite'
import { defineConfig } from 'vite'
+const changelogFile = path.resolve(import.meta.dirname, '../CHANGELOG.md')
+
export default defineConfig({
server: {
port: 3000,
+ fs: {
+ allow: ['..'],
+ },
},
plugins: [
+ {
+ name: 'watch-changelog',
+ configureServer(server) {
+ server.watcher.add(changelogFile)
+ },
+ handleHotUpdate({ file, server }) {
+ if (file !== changelogFile) {
+ return
+ }
+ const changelogModule = [
+ ...server.moduleGraph.urlToModuleMap.values(),
+ ].find((mod) => mod.file?.endsWith('changelog.mdx'))
+ if (changelogModule) {
+ return [changelogModule]
+ }
+ },
+ },
mdx(),
tailwindcss(),
tanstackStart({
diff --git a/package.json b/package.json
index 704b4041..f1b628f9 100644
--- a/package.json
+++ b/package.json
@@ -11,9 +11,13 @@
"prepare": "husky",
"publish": "cd permix && pnpm publish",
"test": "cd permix && pnpm run test",
- "update": "taze -r -I major"
+ "update": "taze -r -I major",
+ "verify": "pnpm run format:check && pnpm run lint && pnpm run test && pnpm run check-types && pnpm run build",
+ "commitlint": "commitlint --config commitlint.config.mts"
},
"devDependencies": {
+ "@commitlint/cli": "^20.5.0",
+ "@commitlint/config-conventional": "^20.5.0",
"husky": "^9.1.7",
"npm-run-all2": "^9.0.1",
"oxfmt": "^0.64.0",
diff --git a/permix/skills/README.md b/permix/skills/README.md
index f2616977..48c13006 100644
--- a/permix/skills/README.md
+++ b/permix/skills/README.md
@@ -72,4 +72,4 @@ pnpm run skills:validate # structure + packaging before publish
pnpm run skills:stale # flag drift vs docs/sources
```
-CI runs `intent validate` on PRs and `intent stale` after releases (`.github/workflows/check-skills.yml`). Update `library_version` in SKILL frontmatter when cutting a release.
+CI runs `intent validate` on PRs and `intent stale` after releases (`.github/workflows/check-skills.yml`). Release Please bumps `library_version` in SKILL frontmatter with the package version.
diff --git a/permix/skills/permix-getting-started/SKILL.md b/permix/skills/permix-getting-started/SKILL.md
index 9647154a..3327eb08 100644
--- a/permix/skills/permix-getting-started/SKILL.md
+++ b/permix/skills/permix-getting-started/SKILL.md
@@ -5,7 +5,7 @@ description: >-
metadata:
type: core
library: permix
- library_version: '4.1.2'
+ library_version: '4.1.2' # x-release-please-version
requires: []
sources:
- 'letstri/permix:docs/content/docs/quick-start.mdx'
diff --git a/permix/skills/permix/SKILL.md b/permix/skills/permix/SKILL.md
index b3ebc1b5..d73f920f 100644
--- a/permix/skills/permix/SKILL.md
+++ b/permix/skills/permix/SKILL.md
@@ -5,7 +5,7 @@ description: >-
metadata:
type: core
library: permix
- library_version: '4.1.2'
+ library_version: '4.1.2' # x-release-please-version
requires:
- permix-getting-started
sources:
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 761d130a..50108e3d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,12 @@ importers:
.:
devDependencies:
+ '@commitlint/cli':
+ specifier: ^20.5.0
+ version: 20.5.3(@types/node@25.9.1)(conventional-commits-parser@6.4.0)(typescript@6.0.3)
+ '@commitlint/config-conventional':
+ specifier: ^20.5.0
+ version: 20.5.3
husky:
specifier: ^9.1.7
version: 9.1.7
@@ -65,6 +71,9 @@ importers:
fumadocs-ui:
specifier: ^16.9.3
version: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)
+ mdast-util-from-markdown:
+ specifier: ^2.0.2
+ version: 2.0.3
mermaid:
specifier: ^11.15.0
version: 11.15.0
@@ -435,7 +444,7 @@ importers:
version: 1.167.1(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-router-ssr-query':
specifier: latest
- version: 1.167.1(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ version: 1.167.2(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start':
specifier: latest
version: 1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -548,7 +557,7 @@ importers:
version: 4.12.23
next:
specifier: '>=14'
- version: 16.2.6(@babel/core@7.29.7)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ version: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react:
specifier: '>=18'
version: 19.2.6
@@ -816,6 +825,87 @@ packages:
resolution: {integrity: sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==}
engines: {node: '>= 20.12.0'}
+ '@commitlint/cli@20.5.3':
+ resolution: {integrity: sha512-OJdL0EXWD5y9LPa0nr/geOwzaS8BsdaybKkcloB0JgsguGxNv2R+hC2FTPqrAcprg35zF33KOQerY0x8W1aesA==}
+ engines: {node: '>=v18'}
+ hasBin: true
+
+ '@commitlint/config-conventional@20.5.3':
+ resolution: {integrity: sha512-j34Qqeaa152chJgz2ysyk0BCpHenJn1lV0Rx0VXf8k3ccQcED+48EZrzMvo9jLmJUyBrrBwvu89I+2er4gW7QQ==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/config-validator@20.5.0':
+ resolution: {integrity: sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/ensure@20.5.3':
+ resolution: {integrity: sha512-4i4AgNvH62owG9MwSiWKrle7HGNpBHHdLnWFIp5fTsHUYe5kRuh15t08L/0pdbbrRk8JKXQxxN4hZQcn+szkrw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/execute-rule@20.0.0':
+ resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/format@20.5.0':
+ resolution: {integrity: sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/is-ignored@20.5.0':
+ resolution: {integrity: sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/lint@20.5.3':
+ resolution: {integrity: sha512-M7JbWBNr2gXKaPc4i/KipsuW1gkDHpj35KPjWtKy3Z+2AQw5wu1gBi1LIO0uoaij67CqY4K8PxPZSGens4evCw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/load@20.5.3':
+ resolution: {integrity: sha512-1FDZWuKyu98Myb8i7Tp31jPU2rZpOwAdYRyJcy2KoGg7Xk2A+bgHN8smhMaaNSNkmE8fwt53BokywZq8Gv/5XQ==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/message@20.4.3':
+ resolution: {integrity: sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/parse@20.5.0':
+ resolution: {integrity: sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/read@20.5.0':
+ resolution: {integrity: sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/resolve-extends@20.5.3':
+ resolution: {integrity: sha512-+ogW9v/u9JqpvAgTrLra/YTFo0KkjU6iNblF89pPsj4NebNc+DAWctsludwezI8YnsjBmfHpApSwcXprN/f/ew==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/rules@20.5.3':
+ resolution: {integrity: sha512-MPlMnb9D3wbszYMp+1hPtuhtPJndRo6I6yfkZVA4+jR8w7Kqp0u2u/Y+gzbaItx5Lltq5rw7FSZQWJMoXUC4NQ==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/to-lines@20.0.0':
+ resolution: {integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/top-level@20.4.3':
+ resolution: {integrity: sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/types@20.5.0':
+ resolution: {integrity: sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==}
+ engines: {node: '>=v18'}
+
+ '@conventional-changelog/git-client@2.7.0':
+ resolution: {integrity: sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ conventional-commits-filter: ^5.0.0
+ conventional-commits-parser: ^6.4.0
+ peerDependenciesMeta:
+ conventional-commits-filter:
+ optional: true
+ conventional-commits-parser:
+ optional: true
+
'@csstools/color-helpers@6.0.2':
resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==}
engines: {node: '>=20.19.0'}
@@ -2349,6 +2439,14 @@ packages:
'@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
+ '@simple-libs/child-process-utils@1.0.2':
+ resolution: {integrity: sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==}
+ engines: {node: '>=18'}
+
+ '@simple-libs/stream-utils@1.2.0':
+ resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==}
+ engines: {node: '>=18'}
+
'@sinclair/typebox@0.34.49':
resolution: {integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==}
@@ -2634,12 +2732,12 @@ packages:
'@tanstack/router-core':
optional: true
- '@tanstack/react-router-ssr-query@1.167.1':
- resolution: {integrity: sha512-W9j5JPnBikyafvuUfykFfHIWod58OAbAAa5leNkXBcoDoocghMmu6w9uZOmUZvAWT7CSvgj5tBUtF7CM2OoHXQ==}
+ '@tanstack/react-router-ssr-query@1.167.2':
+ resolution: {integrity: sha512-yRvy0VJ00R8huPuquk+qyYQGMpYRc/G33mc6/yZ4f7LaBZz9h/VbACjmLzhm/+6u5WSmUt6ouJJZnI1/5Npfag==}
engines: {node: '>=20.19'}
peerDependencies:
- '@tanstack/query-core': '>=5.90.0'
- '@tanstack/react-query': '>=5.90.0'
+ '@tanstack/query-core': '>=5.102.0'
+ '@tanstack/react-query': '>=5.102.0'
'@tanstack/react-router': '>=1.127.0'
react: '>=18.0.0 || >=19.0.0'
react-dom: '>=18.0.0 || >=19.0.0'
@@ -2927,11 +3025,11 @@ packages:
webpack:
optional: true
- '@tanstack/router-ssr-query-core@1.169.1':
- resolution: {integrity: sha512-rngux8s/3mPQzcjLYDLkNU31coYVyCgrVTfpdwqUdY5jIEHqGTXrO73DTkPR1PppwYUeVhmNCgl8TctRcnupjg==}
+ '@tanstack/router-ssr-query-core@1.169.2':
+ resolution: {integrity: sha512-7pO65Aiq/1+aS3Mb6vSGtIjzQ/YGv9JTfBbn2EYlHcZnJ4s4ZGCeTPI847geB1RfJqVhfwxM8bZgGM51mYkolg==}
engines: {node: '>=20.19'}
peerDependencies:
- '@tanstack/query-core': '>=5.90.0'
+ '@tanstack/query-core': '>=5.102.0'
'@tanstack/router-core': '>=1.127.0'
'@tanstack/router-utils@1.162.1':
@@ -3586,6 +3684,9 @@ packages:
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
engines: {node: '>= 0.4'}
+ array-ify@1.0.0:
+ resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==}
+
asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
@@ -3688,6 +3789,10 @@ packages:
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'}
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
caniuse-lite@1.0.30001793:
resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==}
@@ -3780,6 +3885,9 @@ packages:
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
engines: {node: '>= 12'}
+ compare-func@2.0.0:
+ resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==}
+
component-emitter@1.3.1:
resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
@@ -3808,6 +3916,19 @@ packages:
resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==}
engines: {node: '>=18'}
+ conventional-changelog-angular@8.3.1:
+ resolution: {integrity: sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==}
+ engines: {node: '>=18'}
+
+ conventional-changelog-conventionalcommits@9.3.1:
+ resolution: {integrity: sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw==}
+ engines: {node: '>=18'}
+
+ conventional-commits-parser@6.4.0:
+ resolution: {integrity: sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==}
+ engines: {node: '>=18'}
+ hasBin: true
+
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
@@ -3843,6 +3964,23 @@ packages:
cose-base@2.2.0:
resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
+ cosmiconfig-typescript-loader@6.3.0:
+ resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==}
+ engines: {node: '>=v18'}
+ peerDependencies:
+ '@types/node': '*'
+ cosmiconfig: '>=9'
+ typescript: '>=5'
+
+ cosmiconfig@9.0.2:
+ resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ typescript: '>=4.9.5'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -4132,6 +4270,10 @@ packages:
dompurify@3.4.7:
resolution: {integrity: sha512-2jBxDJY4RR06tQNy4w5FlFH7kfxsQZlufd0sbv+chfHCxeJwrFw2baUDsSwvBISD4K4RDbd0PTfy3uNXsR6siA==}
+ dot-prop@5.3.0:
+ resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==}
+ engines: {node: '>=8'}
+
drizzle-orm@1.0.0-rc.3:
resolution: {integrity: sha512-akZOa5UxapFbdBG8IDkfBRpSZJpMHaOJtGgp7oi1oHaiU8S3KN92waHo2l5aRuv1D9tMGYpv3BQFOsiGcNjLTQ==}
peerDependencies:
@@ -4323,6 +4465,10 @@ packages:
resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
engines: {node: '>=20.19.0'}
+ env-paths@2.2.1:
+ resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
+ engines: {node: '>=6'}
+
env-runner@0.1.9:
resolution: {integrity: sha512-W9AiZlPx0uXtghAJiTBkeZOgyQdecVvoln3cHoOEZswPq0cVMi+WBhUQjdUn+JcZFAFgOt+i5fcO7C2zniZoCg==}
hasBin: true
@@ -4342,6 +4488,9 @@ packages:
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
engines: {node: '>=18'}
+ error-ex@1.3.4:
+ resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
+
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
@@ -4733,6 +4882,12 @@ packages:
resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==}
engines: {node: '>=20.20.0'}
+ git-raw-commits@5.0.1:
+ resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==}
+ engines: {node: '>=18'}
+ deprecated: Deprecated and no longer maintained. Use @conventional-changelog/git-client instead.
+ hasBin: true
+
github-slugger@2.0.0:
resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
@@ -4745,6 +4900,10 @@ packages:
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
+ global-directory@5.0.0:
+ resolution: {integrity: sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==}
+ engines: {node: '>=20'}
+
globrex@0.1.2:
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
@@ -4885,6 +5044,10 @@ packages:
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
import-meta-resolve@4.2.0:
resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
@@ -4902,6 +5065,10 @@ packages:
ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+ ini@6.0.0:
+ resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==}
+ engines: {node: ^20.17.0 || >=22.9.0}
+
inline-style-parser@0.2.7:
resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
@@ -4926,6 +5093,9 @@ packages:
is-alphanumerical@2.0.1:
resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
+ is-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
is-decimal@2.0.1:
resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
@@ -4952,6 +5122,10 @@ packages:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
+ is-obj@2.0.0:
+ resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
+ engines: {node: '>=8'}
+
is-plain-obj@4.1.0:
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
engines: {node: '>=12'}
@@ -5003,6 +5177,10 @@ packages:
jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+ jiti@2.6.1:
+ resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
+ hasBin: true
+
jiti@2.7.0:
resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
hasBin: true
@@ -5039,6 +5217,9 @@ packages:
engines: {node: '>=6'}
hasBin: true
+ json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+
json-parse-even-better-errors@6.0.0:
resolution: {integrity: sha512-2/8adwnK1/+Fdjyts4r6wSpfANWw8zdNhU9U/Llk59c6O+DjSisPWPykwoL8gZmocP9Dy64S7oie2g+Mia123A==}
engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0}
@@ -5154,6 +5335,9 @@ packages:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
locate-character@3.0.0:
resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
@@ -5282,6 +5466,10 @@ packages:
resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
engines: {node: '>= 0.10.0'}
+ meow@13.2.0:
+ resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==}
+ engines: {node: '>=18'}
+
merge-anything@5.1.7:
resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==}
engines: {node: '>=12.13'}
@@ -5443,6 +5631,9 @@ packages:
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
engines: {node: '>=16 || 14 >=14.17'}
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
minipass@7.1.3:
resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -5671,9 +5862,17 @@ packages:
package-manager-detector@1.6.0:
resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
parse-entities@4.0.2:
resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
+ parse-json@5.2.0:
+ resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+ engines: {node: '>=8'}
+
parse-ms@4.0.0:
resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
engines: {node: '>=18'}
@@ -5983,6 +6182,14 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-from@5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
@@ -6948,7 +7155,7 @@ snapshots:
'@antfu/install-pkg@1.1.0':
dependencies:
package-manager-detector: 1.6.0
- tinyexec: 1.2.3
+ tinyexec: 1.3.0
'@antfu/ni@30.1.0':
dependencies:
@@ -7147,6 +7354,124 @@ snapshots:
fast-wrap-ansi: 0.2.2
sisteransi: 1.0.5
+ '@commitlint/cli@20.5.3(@types/node@25.9.1)(conventional-commits-parser@6.4.0)(typescript@6.0.3)':
+ dependencies:
+ '@commitlint/format': 20.5.0
+ '@commitlint/lint': 20.5.3
+ '@commitlint/load': 20.5.3(@types/node@25.9.1)(typescript@6.0.3)
+ '@commitlint/read': 20.5.0(conventional-commits-parser@6.4.0)
+ '@commitlint/types': 20.5.0
+ tinyexec: 1.3.0
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - '@types/node'
+ - conventional-commits-filter
+ - conventional-commits-parser
+ - typescript
+
+ '@commitlint/config-conventional@20.5.3':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ conventional-changelog-conventionalcommits: 9.3.1
+
+ '@commitlint/config-validator@20.5.0':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ ajv: 8.20.0
+
+ '@commitlint/ensure@20.5.3':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ es-toolkit: 1.47.0
+
+ '@commitlint/execute-rule@20.0.0': {}
+
+ '@commitlint/format@20.5.0':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ picocolors: 1.1.1
+
+ '@commitlint/is-ignored@20.5.0':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ semver: 7.8.1
+
+ '@commitlint/lint@20.5.3':
+ dependencies:
+ '@commitlint/is-ignored': 20.5.0
+ '@commitlint/parse': 20.5.0
+ '@commitlint/rules': 20.5.3
+ '@commitlint/types': 20.5.0
+
+ '@commitlint/load@20.5.3(@types/node@25.9.1)(typescript@6.0.3)':
+ dependencies:
+ '@commitlint/config-validator': 20.5.0
+ '@commitlint/execute-rule': 20.0.0
+ '@commitlint/resolve-extends': 20.5.3
+ '@commitlint/types': 20.5.0
+ cosmiconfig: 9.0.2(typescript@6.0.3)
+ cosmiconfig-typescript-loader: 6.3.0(@types/node@25.9.1)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3)
+ es-toolkit: 1.47.0
+ is-plain-obj: 4.1.0
+ picocolors: 1.1.1
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+
+ '@commitlint/message@20.4.3': {}
+
+ '@commitlint/parse@20.5.0':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ conventional-changelog-angular: 8.3.1
+ conventional-commits-parser: 6.4.0
+
+ '@commitlint/read@20.5.0(conventional-commits-parser@6.4.0)':
+ dependencies:
+ '@commitlint/top-level': 20.4.3
+ '@commitlint/types': 20.5.0
+ git-raw-commits: 5.0.1(conventional-commits-parser@6.4.0)
+ minimist: 1.2.8
+ tinyexec: 1.3.0
+ transitivePeerDependencies:
+ - conventional-commits-filter
+ - conventional-commits-parser
+
+ '@commitlint/resolve-extends@20.5.3':
+ dependencies:
+ '@commitlint/config-validator': 20.5.0
+ '@commitlint/types': 20.5.0
+ es-toolkit: 1.47.0
+ global-directory: 5.0.0
+ import-meta-resolve: 4.2.0
+ resolve-from: 5.0.0
+
+ '@commitlint/rules@20.5.3':
+ dependencies:
+ '@commitlint/ensure': 20.5.3
+ '@commitlint/message': 20.4.3
+ '@commitlint/to-lines': 20.0.0
+ '@commitlint/types': 20.5.0
+
+ '@commitlint/to-lines@20.0.0': {}
+
+ '@commitlint/top-level@20.4.3':
+ dependencies:
+ escalade: 3.2.0
+
+ '@commitlint/types@20.5.0':
+ dependencies:
+ conventional-commits-parser: 6.4.0
+ picocolors: 1.1.1
+
+ '@conventional-changelog/git-client@2.7.0(conventional-commits-parser@6.4.0)':
+ dependencies:
+ '@simple-libs/child-process-utils': 1.0.2
+ '@simple-libs/stream-utils': 1.2.0
+ semver: 7.8.1
+ optionalDependencies:
+ conventional-commits-parser: 6.4.0
+
'@csstools/color-helpers@6.0.2': {}
'@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
@@ -8328,6 +8653,12 @@ snapshots:
'@shikijs/vscode-textmate@10.0.2': {}
+ '@simple-libs/child-process-utils@1.0.2':
+ dependencies:
+ '@simple-libs/stream-utils': 1.2.0
+
+ '@simple-libs/stream-utils@1.2.0': {}
+
'@sinclair/typebox@0.34.49': {}
'@sindresorhus/merge-streams@4.0.0': {}
@@ -8665,12 +8996,12 @@ snapshots:
transitivePeerDependencies:
- csstype
- '@tanstack/react-router-ssr-query@1.167.1(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
+ '@tanstack/react-router-ssr-query@1.167.2(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
'@tanstack/query-core': 5.101.0
'@tanstack/react-query': 5.101.0(react@19.2.6)
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/router-ssr-query-core': 1.169.1(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)
+ '@tanstack/router-ssr-query-core': 1.169.2(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
transitivePeerDependencies:
@@ -9062,7 +9393,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tanstack/router-ssr-query-core@1.169.1(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)':
+ '@tanstack/router-ssr-query-core@1.169.2(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)':
dependencies:
'@tanstack/query-core': 5.101.0
'@tanstack/router-core': 1.171.27
@@ -9673,7 +10004,7 @@ snapshots:
obug: 2.1.1
std-env: 4.1.0
tinyrainbow: 3.1.0
- vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitest: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vitest/expect@4.1.8':
dependencies:
@@ -9876,6 +10207,8 @@ snapshots:
aria-query@5.3.2: {}
+ array-ify@1.0.0: {}
+
asap@2.0.6: {}
assertion-error@2.0.1: {}
@@ -9988,6 +10321,8 @@ snapshots:
call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.3.0
+ callsites@3.1.0: {}
+
caniuse-lite@1.0.30001793: {}
ccount@2.0.1: {}
@@ -10059,6 +10394,11 @@ snapshots:
commander@8.3.0: {}
+ compare-func@2.0.0:
+ dependencies:
+ array-ify: 1.0.0
+ dot-prop: 5.3.0
+
component-emitter@1.3.1: {}
compute-scroll-into-view@3.1.1: {}
@@ -10078,6 +10418,19 @@ snapshots:
content-type@2.0.0: {}
+ conventional-changelog-angular@8.3.1:
+ dependencies:
+ compare-func: 2.0.0
+
+ conventional-changelog-conventionalcommits@9.3.1:
+ dependencies:
+ compare-func: 2.0.0
+
+ conventional-commits-parser@6.4.0:
+ dependencies:
+ '@simple-libs/stream-utils': 1.2.0
+ meow: 13.2.0
+
convert-source-map@2.0.0: {}
cookie-es@3.1.1: {}
@@ -10105,6 +10458,22 @@ snapshots:
dependencies:
layout-base: 2.0.1
+ cosmiconfig-typescript-loader@6.3.0(@types/node@25.9.1)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3):
+ dependencies:
+ '@types/node': 25.9.1
+ cosmiconfig: 9.0.2(typescript@6.0.3)
+ jiti: 2.6.1
+ typescript: 6.0.3
+
+ cosmiconfig@9.0.2(typescript@6.0.3):
+ dependencies:
+ env-paths: 2.2.1
+ import-fresh: 3.3.1
+ js-yaml: 4.1.1
+ parse-json: 5.2.0
+ optionalDependencies:
+ typescript: 6.0.3
+
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
@@ -10383,6 +10752,10 @@ snapshots:
optionalDependencies:
'@types/trusted-types': 2.0.7
+ dot-prop@5.3.0:
+ dependencies:
+ is-obj: 2.0.0
+
drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(effect@3.21.2)(zod@4.4.3):
optionalDependencies:
'@sinclair/typebox': 0.34.49
@@ -10447,6 +10820,8 @@ snapshots:
entities@8.0.0: {}
+ env-paths@2.2.1: {}
+
env-runner@0.1.9:
dependencies:
crossws: 0.4.5(srvx@0.11.16)
@@ -10456,6 +10831,10 @@ snapshots:
environment@1.1.0: {}
+ error-ex@1.3.4:
+ dependencies:
+ is-arrayish: 0.2.1
+
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
@@ -10939,6 +11318,14 @@ snapshots:
dependencies:
resolve-pkg-maps: 1.0.0
+ git-raw-commits@5.0.1(conventional-commits-parser@6.4.0):
+ dependencies:
+ '@conventional-changelog/git-client': 2.7.0(conventional-commits-parser@6.4.0)
+ meow: 13.2.0
+ transitivePeerDependencies:
+ - conventional-commits-filter
+ - conventional-commits-parser
+
github-slugger@2.0.0: {}
glob-parent@5.1.2:
@@ -10954,6 +11341,10 @@ snapshots:
package-json-from-dist: 1.0.1
path-scurry: 1.11.1
+ global-directory@5.0.0:
+ dependencies:
+ ini: 6.0.0
+
globrex@0.1.2: {}
goober@2.1.19(csstype@3.2.3):
@@ -11166,6 +11557,11 @@ snapshots:
ieee754@1.2.1: {}
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
import-meta-resolve@4.2.0: {}
import-without-cache@0.4.0: {}
@@ -11176,6 +11572,8 @@ snapshots:
ini@1.3.8: {}
+ ini@6.0.0: {}
+
inline-style-parser@0.2.7: {}
internmap@1.0.1: {}
@@ -11193,6 +11591,8 @@ snapshots:
is-alphabetical: 2.0.1
is-decimal: 2.0.1
+ is-arrayish@0.2.1: {}
+
is-decimal@2.0.1: {}
is-extglob@2.1.1: {}
@@ -11211,6 +11611,8 @@ snapshots:
is-number@7.0.0: {}
+ is-obj@2.0.0: {}
+
is-plain-obj@4.1.0: {}
is-potential-custom-element-name@1.0.1: {}
@@ -11252,6 +11654,8 @@ snapshots:
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
+ jiti@2.6.1: {}
+
jiti@2.7.0: {}
js-beautify@1.15.4:
@@ -11301,6 +11705,8 @@ snapshots:
jsesc@3.1.0: {}
+ json-parse-even-better-errors@2.3.1: {}
+
json-parse-even-better-errors@6.0.0: {}
json-schema-ref-resolver@3.0.0:
@@ -11385,6 +11791,8 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
+ lines-and-columns@1.2.4: {}
+
locate-character@3.0.0: {}
lodash-es@4.18.1: {}
@@ -11617,6 +12025,8 @@ snapshots:
memorystream@0.3.1: {}
+ meow@13.2.0: {}
+
merge-anything@5.1.7:
dependencies:
is-what: 4.1.16
@@ -11942,6 +12352,8 @@ snapshots:
dependencies:
brace-expansion: 2.1.1
+ minimist@1.2.8: {}
+
minipass@7.1.3: {}
mlly@1.8.2:
@@ -11982,30 +12394,6 @@ snapshots:
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- next@16.2.6(@babel/core@7.29.7)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
- dependencies:
- '@next/env': 16.2.6
- '@swc/helpers': 0.5.15
- baseline-browser-mapping: 2.10.33
- caniuse-lite: 1.0.30001793
- postcss: 8.4.31
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.6)
- optionalDependencies:
- '@next/swc-darwin-arm64': 16.2.6
- '@next/swc-darwin-x64': 16.2.6
- '@next/swc-linux-arm64-gnu': 16.2.6
- '@next/swc-linux-arm64-musl': 16.2.6
- '@next/swc-linux-x64-gnu': 16.2.6
- '@next/swc-linux-x64-musl': 16.2.6
- '@next/swc-win32-arm64-msvc': 16.2.6
- '@next/swc-win32-x64-msvc': 16.2.6
- sharp: 0.34.5
- transitivePeerDependencies:
- - '@babel/core'
- - babel-plugin-macros
-
next@16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies:
'@next/env': 16.2.6
@@ -12039,7 +12427,7 @@ snapshots:
postcss: 8.4.31
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.6)
+ styled-jsx: 5.1.6(react@19.2.6)
optionalDependencies:
'@next/swc-darwin-arm64': 16.2.6
'@next/swc-darwin-x64': 16.2.6
@@ -12053,7 +12441,6 @@ snapshots:
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
- optional: true
nf3@0.3.17: {}
@@ -12272,6 +12659,10 @@ snapshots:
package-manager-detector@1.6.0: {}
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
parse-entities@4.0.2:
dependencies:
'@types/unist': 2.0.11
@@ -12282,6 +12673,13 @@ snapshots:
is-decimal: 2.0.1
is-hexadecimal: 2.0.1
+ parse-json@5.2.0:
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ error-ex: 1.3.4
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.2.4
+
parse-ms@4.0.0: {}
parse5@7.3.0:
@@ -12615,6 +13013,10 @@ snapshots:
require-from-string@2.0.2: {}
+ resolve-from@4.0.0: {}
+
+ resolve-from@5.0.0: {}
+
resolve-pkg-maps@1.0.0: {}
resolve.exports@2.0.3: {}
@@ -12946,17 +13348,15 @@ snapshots:
dependencies:
inline-style-parser: 0.2.7
- styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.6):
+ styled-jsx@5.1.6(react@19.2.4):
dependencies:
client-only: 0.0.1
- react: 19.2.6
- optionalDependencies:
- '@babel/core': 7.29.7
+ react: 19.2.4
- styled-jsx@5.1.6(react@19.2.4):
+ styled-jsx@5.1.6(react@19.2.6):
dependencies:
client-only: 0.0.1
- react: 19.2.4
+ react: 19.2.6
stylis@4.4.0: {}
diff --git a/release-please-config.json b/release-please-config.json
new file mode 100644
index 00000000..5f11f934
--- /dev/null
+++ b/release-please-config.json
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
+ "packages": {
+ "permix": {
+ "release-type": "node",
+ "package-name": "permix",
+ "changelog-path": "../CHANGELOG.md",
+ "include-component-in-tag": false,
+ "include-v-in-tag": true,
+ "extra-files": [
+ "skills/permix/SKILL.md",
+ "skills/permix-getting-started/SKILL.md"
+ ]
+ }
+ }
+}
diff --git a/scripts/generate-historical-changelog.mjs b/scripts/generate-historical-changelog.mjs
new file mode 100644
index 00000000..0047d2a1
--- /dev/null
+++ b/scripts/generate-historical-changelog.mjs
@@ -0,0 +1,216 @@
+#!/usr/bin/env node
+import { execFileSync } from 'node:child_process'
+import { writeFileSync } from 'node:fs'
+import path from 'node:path'
+
+const root = path.resolve(import.meta.dirname, '..')
+const repoUrl = 'https://github.com/letstri/permix'
+
+function git(args, options = {}) {
+ return execFileSync('git', args, {
+ cwd: root,
+ encoding: 'utf-8',
+ ...options,
+ }).trim()
+}
+
+function npmView(args) {
+ return execFileSync('npm', ['view', 'permix', ...args, '--json'], {
+ encoding: 'utf-8',
+ }).trim()
+}
+
+function parseVersion(source) {
+ const match = source.match(/"version"\s*:\s*"([^"]+)"/)
+ return match?.[1] ?? null
+}
+
+function versionAt(sha) {
+ for (const filePath of ['permix/package.json', 'package.json']) {
+ try {
+ const source = git(['show', `${sha}:${filePath}`], {
+ stdio: ['ignore', 'pipe', 'ignore'],
+ })
+ const version = parseVersion(source)
+ if (version) {
+ return version
+ }
+ } catch {
+ // File missing at this commit.
+ }
+ }
+ return null
+}
+
+function isBumpSubject(subject) {
+ return /^(chore:\s*)?(bump|update)\s+version(\s+to\s+v?\S+)?$/i.test(
+ subject.trim()
+ )
+}
+
+function classify(subject) {
+ const text = subject.trim()
+ const type = text.split(':')[0] ?? ''
+ if (type.includes('!') || /^breaking(\s+change)?(\(.+\))?!?:/i.test(text)) {
+ return 'breaking'
+ }
+ if (/^(feat|feature)(\(.+\))?:/i.test(text)) {
+ return 'added'
+ }
+ if (/^fix(\(.+\))?:/i.test(text)) {
+ return 'fixed'
+ }
+ if (/^docs(\(.+\))?:/i.test(text)) {
+ return 'docs'
+ }
+ return 'changed'
+}
+
+function bucketLabel(key) {
+ switch (key) {
+ case 'breaking': {
+ return 'Breaking Changes'
+ }
+ case 'added': {
+ return 'Features'
+ }
+ case 'fixed': {
+ return 'Bug Fixes'
+ }
+ case 'docs': {
+ return 'Documentation'
+ }
+ case 'changed': {
+ return 'Miscellaneous'
+ }
+ default: {
+ throw new Error(`Unhandled changelog bucket: ${String(key)}`)
+ }
+ }
+}
+
+function escapeMd(text) {
+ return text.replaceAll('<', '\\<')
+}
+
+function dateOnly(iso) {
+ return iso.slice(0, 10)
+}
+
+const versions = JSON.parse(npmView(['versions']))
+const timesRaw = JSON.parse(npmView(['time']))
+const times = Array.isArray(timesRaw) ? timesRaw[0] : timesRaw
+
+const bumpLog = git([
+ 'log',
+ '--reverse',
+ '--pretty=%H',
+ '-G',
+ '"version":',
+ '--',
+ 'permix/package.json',
+ 'package.json',
+])
+ .split('\n')
+ .filter(Boolean)
+
+const versionToSha = new Map()
+for (const sha of bumpLog) {
+ const version = versionAt(sha)
+ if (version) {
+ versionToSha.set(version, sha)
+ }
+}
+
+const mapping = []
+const sections = []
+const order = ['breaking', 'added', 'fixed', 'docs', 'changed']
+
+for (let i = 0; i < versions.length; i++) {
+ const version = versions[i]
+ const prev = i > 0 ? versions[i - 1] : null
+ const sha = versionToSha.get(version) ?? null
+ const prevSha = prev ? (versionToSha.get(prev) ?? null) : null
+ const published =
+ typeof times[version] === 'string' ? dateOnly(times[version]) : 'unknown'
+
+ mapping.push({ version, sha, published })
+
+ let subjects = []
+ if (sha) {
+ const range = prevSha ? `${prevSha}..${sha}` : sha
+ const log = git(['log', '--no-merges', '--pretty=%s', range])
+ subjects = log ? log.split('\n').filter(Boolean) : []
+ }
+
+ const buckets = {
+ breaking: [],
+ added: [],
+ fixed: [],
+ docs: [],
+ changed: [],
+ }
+
+ for (const subject of subjects) {
+ if (isBumpSubject(subject)) {
+ continue
+ }
+ buckets[classify(subject)].push(subject)
+ }
+
+ const compare = prev
+ ? `${repoUrl}/compare/v${prev}...v${version}`
+ : `${repoUrl}/releases/tag/v${version}`
+ const heading = `## [${version}](${compare}) (${published})`
+
+ const lines = [heading, '']
+ let hasNotes = false
+ for (const key of order) {
+ const items = buckets[key]
+ if (items.length === 0) {
+ continue
+ }
+ hasNotes = true
+ lines.push(`### ${bucketLabel(key)}`, '')
+ for (const item of items) {
+ lines.push(`* ${escapeMd(item)}`)
+ }
+ lines.push('')
+ }
+ if (!hasNotes) {
+ lines.push('* Published to npm.', '')
+ }
+ sections.push(lines.join('\n'))
+}
+
+sections.reverse()
+
+const changelog = `${[
+ '# Changelog',
+ '',
+ 'All notable changes to `permix` are documented in this file.',
+ '',
+ 'The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),',
+ 'and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).',
+ 'Entries through 4.1.2 were reconstructed from npm publish dates and git history;',
+ 'later versions are maintained by Release Please.',
+ '',
+ ...sections,
+]
+ .join('\n')
+ .replaceAll(/\n{3,}/g, '\n\n')}\n`
+
+writeFileSync(path.resolve(root, 'CHANGELOG.md'), changelog)
+writeFileSync(
+ path.resolve(root, 'scripts/historical-releases.json'),
+ `${JSON.stringify(mapping, null, 2)}\n`
+)
+
+console.log(`Wrote CHANGELOG.md with ${versions.length} versions`)
+console.log(
+ `Mapped SHAs: ${mapping.filter((item) => item.sha).length}/${mapping.length}`
+)
+const missing = mapping.filter((item) => !item.sha).map((item) => item.version)
+if (missing.length > 0) {
+ console.log(`Missing SHAs: ${missing.join(', ')}`)
+}
diff --git a/scripts/historical-releases.json b/scripts/historical-releases.json
new file mode 100644
index 00000000..65564d79
--- /dev/null
+++ b/scripts/historical-releases.json
@@ -0,0 +1,362 @@
+[
+ {
+ "version": "0.0.1-alpha.1",
+ "sha": null,
+ "published": "2025-01-13"
+ },
+ {
+ "version": "0.0.1",
+ "sha": "343a0dfa21829f8c3b282c5439a164b89e752e68",
+ "published": "2025-01-14"
+ },
+ {
+ "version": "0.1.0",
+ "sha": "0f802c4926fcb12e0624b6573891f568a02ad26f",
+ "published": "2025-01-15"
+ },
+ {
+ "version": "0.1.1",
+ "sha": "f3d49b87305601197496ca8a5e3c806cd12b25ce",
+ "published": "2025-01-15"
+ },
+ {
+ "version": "0.1.2",
+ "sha": "8f00fed0f0ae4618998dc899da5e6b1a92243d4c",
+ "published": "2025-01-15"
+ },
+ {
+ "version": "0.2.0",
+ "sha": "40fc100371f449359fba619cb9b2358f6387e27c",
+ "published": "2025-01-15"
+ },
+ {
+ "version": "0.2.1",
+ "sha": "5b6246eb97c2d268a4e9ee2242cb3efc888a0e1d",
+ "published": "2025-01-15"
+ },
+ {
+ "version": "0.3.0",
+ "sha": "1cc79c25241af201b3320bda1a9d2d541b6293b6",
+ "published": "2025-01-15"
+ },
+ {
+ "version": "0.3.1",
+ "sha": "7bbaa71aa88582b9eff1ef5046b6e6cfe641b023",
+ "published": "2025-01-15"
+ },
+ {
+ "version": "0.3.2",
+ "sha": "3b9463cd1cce8c584a4b7ff5c553d519c1e6b973",
+ "published": "2025-01-16"
+ },
+ {
+ "version": "0.3.3",
+ "sha": "a811a459aab34a3192e41649545cf3e4ba7a6657",
+ "published": "2025-01-16"
+ },
+ {
+ "version": "0.3.4",
+ "sha": "9cdbedc18026d9dcafb3d7cc63e8835464e00eb1",
+ "published": "2025-01-16"
+ },
+ {
+ "version": "0.3.5",
+ "sha": "335c6a81e7100d02a196da20a3db869d35633c47",
+ "published": "2025-01-16"
+ },
+ {
+ "version": "0.4.0",
+ "sha": "eb0c66cf2c1283c38d40351c3004da58d0dd1ebf",
+ "published": "2025-01-16"
+ },
+ {
+ "version": "0.4.1",
+ "sha": "f778190127c5606b3bdcb92cc0b10ce59da9cbb7",
+ "published": "2025-01-17"
+ },
+ {
+ "version": "0.4.2",
+ "sha": "94cd3e71eaa0183dd9b15a508c65e496a496a16e",
+ "published": "2025-01-17"
+ },
+ {
+ "version": "0.5.0",
+ "sha": "8f14d5f45ef16f47fadafefd81dd3bc6f72ac5bb",
+ "published": "2025-01-17"
+ },
+ {
+ "version": "0.6.0",
+ "sha": "59664310920665d37d6a020b376285f8f8813f9f",
+ "published": "2025-01-17"
+ },
+ {
+ "version": "0.7.0",
+ "sha": "749a6fcf340bcf5151b3ace7bba8c7377266002a",
+ "published": "2025-01-17"
+ },
+ {
+ "version": "0.7.1",
+ "sha": "038be64a36dfedf5eb709f3e2953019dbf905cb4",
+ "published": "2025-01-18"
+ },
+ {
+ "version": "0.7.2-beta.1",
+ "sha": "0704019ca384cc7f1bb7b95484a5bdab87edf544",
+ "published": "2025-01-18"
+ },
+ {
+ "version": "0.7.2-beta.2",
+ "sha": "bc9328c560185a599e7507dc9c12bed087f3f167",
+ "published": "2025-01-18"
+ },
+ {
+ "version": "0.7.2",
+ "sha": "fa8b4921093e2355caa16a6b3cc21a6cc29491e1",
+ "published": "2025-01-18"
+ },
+ {
+ "version": "0.7.3",
+ "sha": "4f991328be71c647fddd8a490283eda90fa3e380",
+ "published": "2025-01-18"
+ },
+ {
+ "version": "0.8.0",
+ "sha": "02736eda0e6463ca3ea95a24a686756095f1c239",
+ "published": "2025-01-21"
+ },
+ {
+ "version": "1.0.0-rc.1",
+ "sha": "0826ffac1c44a365995f05a6ac95369259c01b58",
+ "published": "2025-01-22"
+ },
+ {
+ "version": "1.0.0-rc.2",
+ "sha": "a85257e4bfeded942d5bf0c320ed3604fc1be90b",
+ "published": "2025-01-22"
+ },
+ {
+ "version": "1.0.0",
+ "sha": "bb99509603457dd1515132b9521cf1d04951f0c1",
+ "published": "2025-01-22"
+ },
+ {
+ "version": "1.0.1",
+ "sha": "c67754a5905386f287237538cd4ef4cb6f6e3e45",
+ "published": "2025-02-02"
+ },
+ {
+ "version": "1.0.2",
+ "sha": "6d1074384ff24eb0c8d2b55867bea44d78c85e87",
+ "published": "2025-02-02"
+ },
+ {
+ "version": "1.0.3",
+ "sha": "68e1e7f2f60676fd66da57b6815199c782665c31",
+ "published": "2025-02-03"
+ },
+ {
+ "version": "1.0.4",
+ "sha": "5120f7c67573009c13ab5510a71e443a60351abe",
+ "published": "2025-02-04"
+ },
+ {
+ "version": "2.0.0-beta.1",
+ "sha": "13a6c93c06cd3d9e2ef8efd3892f3eb8646f3d8c",
+ "published": "2025-02-26"
+ },
+ {
+ "version": "2.0.0-rc.1",
+ "sha": "7a825a41acb314b998b66d34fa7b81f28b11a835",
+ "published": "2025-02-26"
+ },
+ {
+ "version": "2.0.0-rc.2",
+ "sha": "e4e3c46ed9177570aeb52e6dd0fb3976c1a30387",
+ "published": "2025-02-27"
+ },
+ {
+ "version": "2.0.0-rc.3",
+ "sha": "f8566eabc810bb36d70800cc02711875b9261a1b",
+ "published": "2025-02-27"
+ },
+ {
+ "version": "2.0.0-rc.4",
+ "sha": "c36fb1a28243d2c28b1c2cec45964658a9ed05a7",
+ "published": "2025-02-27"
+ },
+ {
+ "version": "2.0.0-rc.5",
+ "sha": "e3868452c772bad689a1223007f61637b8fc3ae3",
+ "published": "2025-02-27"
+ },
+ {
+ "version": "2.0.0-rc.6",
+ "sha": "0bff919508c758350753510f31c61024b3d838ff",
+ "published": "2025-02-27"
+ },
+ {
+ "version": "2.0.0-rc.7",
+ "sha": "ec6e5e9947ec87ade06ac7556143f636eaae623b",
+ "published": "2025-02-28"
+ },
+ {
+ "version": "2.0.0-rc.8",
+ "sha": "37431d4b85a33eb5d0d18aa0b6c22f2d5513a41a",
+ "published": "2025-02-28"
+ },
+ {
+ "version": "2.0.0-rc.9",
+ "sha": "9487f3cc6d220a645c523723fced9b69c75eec36",
+ "published": "2025-03-03"
+ },
+ {
+ "version": "2.0.0-rc.10",
+ "sha": "b25b108c8d972d8b7616d205f0920c22cf7aeb9a",
+ "published": "2025-03-03"
+ },
+ {
+ "version": "2.0.0-rc.11",
+ "sha": "a43c4c1f0a492300925c5a55464da676ea0ba524",
+ "published": "2025-03-03"
+ },
+ {
+ "version": "2.0.0-rc.12",
+ "sha": "0f2802bdf0a1e8a4544eebbb85fc40082b4886cf",
+ "published": "2025-03-03"
+ },
+ {
+ "version": "2.0.0-rc.13",
+ "sha": "03607e4b5a665534135f679656c3164156414ab2",
+ "published": "2025-03-03"
+ },
+ {
+ "version": "2.0.0",
+ "sha": "dcabaf827015d151324d6f20639330b96ae6ee12",
+ "published": "2025-03-06"
+ },
+ {
+ "version": "2.1.0",
+ "sha": "d15d694d956ac38426aafe0380f876874430a433",
+ "published": "2025-04-08"
+ },
+ {
+ "version": "2.1.1",
+ "sha": "53e5d91f8705d4aa02f32b417e0d14f949facc09",
+ "published": "2025-04-08"
+ },
+ {
+ "version": "2.1.2",
+ "sha": "2ed7ef8c4c1371e651bb03794304d37e13490c0f",
+ "published": "2025-04-08"
+ },
+ {
+ "version": "2.1.3",
+ "sha": "74c3f74e2959f0ce9b863130e7e25154f5f0b097",
+ "published": "2025-04-08"
+ },
+ {
+ "version": "2.1.4",
+ "sha": "7b8bc20c30d7ab03dfb708b6d534fde27442d63a",
+ "published": "2025-04-08"
+ },
+ {
+ "version": "2.1.5",
+ "sha": "c4ebd3ddb9a4f90a6c83309b9a49f6ba864e0c68",
+ "published": "2025-04-09"
+ },
+ {
+ "version": "3.0.0",
+ "sha": "7065528d7f58dd1591b078cb418bdfbfab122210",
+ "published": "2025-06-21"
+ },
+ {
+ "version": "3.1.0",
+ "sha": "2afd5540849a701aca095eca9226ac143f3d6f0c",
+ "published": "2025-06-22"
+ },
+ {
+ "version": "3.2.0",
+ "sha": "efa284a6b8f2fce0dacb94f93b94487025360583",
+ "published": "2025-06-22"
+ },
+ {
+ "version": "3.2.1",
+ "sha": "6abb14b2a485660eee48199881cfc5888fc94a72",
+ "published": "2025-06-23"
+ },
+ {
+ "version": "3.3.0",
+ "sha": "b37ab625c6f0047c4e8f969d8b876a681dcc4f83",
+ "published": "2025-06-24"
+ },
+ {
+ "version": "3.4.0",
+ "sha": "991452fe2139a4d7e6d25e5d6b81db6c0b75b99d",
+ "published": "2025-07-09"
+ },
+ {
+ "version": "3.4.1",
+ "sha": "8eca1a6e1b72620c2a4ae1bac3a986318a135d5c",
+ "published": "2025-07-23"
+ },
+ {
+ "version": "3.5.0",
+ "sha": "cee5124064869255db9767a287d497864f5ad607",
+ "published": "2025-08-02"
+ },
+ {
+ "version": "3.5.1",
+ "sha": "a4f5deab1312e6e1b181f58a9ac1c854ff939316",
+ "published": "2025-11-02"
+ },
+ {
+ "version": "3.5.2",
+ "sha": "9860dca632c8a5b7bb276e41d7f88907ce2cdf7f",
+ "published": "2025-11-04"
+ },
+ {
+ "version": "3.6.0",
+ "sha": "85cb5b0f1809081be4d4a96cc399da20c37f05c4",
+ "published": "2025-11-06"
+ },
+ {
+ "version": "3.7.0",
+ "sha": "1becea9e1d14e724c2b3374b75860db18eb11dc6",
+ "published": "2026-03-19"
+ },
+ {
+ "version": "3.8.0",
+ "sha": "6ab400f98eb7e7d99f3baee06813c2b9e19902d7",
+ "published": "2026-03-23"
+ },
+ {
+ "version": "3.8.1",
+ "sha": "f9c45b99e034e75e28b6b2aea0e69aca3c2d3c7c",
+ "published": "2026-04-29"
+ },
+ {
+ "version": "4.0.0",
+ "sha": "665ba46751644adf97c8c57ae57bd3e366822eeb",
+ "published": "2026-06-03"
+ },
+ {
+ "version": "4.0.1",
+ "sha": "3dd13d60cb717a9644556a8c4b45c1fb2b95dcc3",
+ "published": "2026-06-03"
+ },
+ {
+ "version": "4.1.0",
+ "sha": "23784998c8055899fb9876e3a27f1938833a1628",
+ "published": "2026-06-08"
+ },
+ {
+ "version": "4.1.1",
+ "sha": "91a7d979886fec49f88d7ac9ba7a27957e62fdef",
+ "published": "2026-06-11"
+ },
+ {
+ "version": "4.1.2",
+ "sha": "f5e5592c939ad00738cdc9b47eb332f57df3212b",
+ "published": "2026-07-02"
+ }
+]
diff --git a/scripts/tag-historical-releases.mjs b/scripts/tag-historical-releases.mjs
new file mode 100644
index 00000000..a742ffd3
--- /dev/null
+++ b/scripts/tag-historical-releases.mjs
@@ -0,0 +1,63 @@
+#!/usr/bin/env node
+import { execFileSync } from 'node:child_process'
+import { readFileSync } from 'node:fs'
+import path from 'node:path'
+
+const root = path.resolve(import.meta.dirname, '..')
+const apply = process.argv.includes('--apply')
+const push = process.argv.includes('--push')
+
+function git(args) {
+ return execFileSync('git', args, {
+ cwd: root,
+ encoding: 'utf-8',
+ }).trim()
+}
+
+const mapping = JSON.parse(
+ readFileSync(path.resolve(root, 'scripts/historical-releases.json'), 'utf-8')
+)
+
+const existing = new Set(
+ git(['tag', '--list', 'v*']).split('\n').filter(Boolean)
+)
+
+let created = 0
+let skipped = 0
+let missingSha = 0
+
+for (const entry of mapping) {
+ const tag = `v${entry.version}`
+ if (!entry.sha) {
+ missingSha++
+ console.log(`skip ${tag}: no mapped SHA`)
+ continue
+ }
+ if (existing.has(tag)) {
+ skipped++
+ continue
+ }
+ if (!apply) {
+ console.log(`would tag ${tag} -> ${entry.sha}`)
+ created++
+ continue
+ }
+ git(['tag', '-a', tag, entry.sha, '-m', tag])
+ existing.add(tag)
+ created++
+ console.log(`tagged ${tag} -> ${entry.sha}`)
+}
+
+if (apply && push) {
+ git(['push', 'origin', '--tags'])
+ console.log('pushed tags to origin')
+}
+
+console.log(
+ `${apply ? 'created' : 'would create'} ${created}, skipped ${skipped}, missing SHA ${missingSha}`
+)
+if (!apply) {
+ console.log(
+ 'Re-run with --apply to create annotated tags. Add --push to push them.'
+ )
+}
From a6103259eaa1e0afad2dca43e36c79333e751dae Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 14:17:42 +0200
Subject: [PATCH 09/28] chore: switch docs to Fumadocs Base UI and skip
vendored skills in oxfmt
Upgrade Fumadocs to 16.15 with the Base UI alias.
Ignore .agents and .claude so CI format check skips third-party skill files.
Co-authored-by: Cursor
---
.gitignore | 2 +
docs/package.json | 10 +-
docs/source.config.ts | 5 +
docs/src/components/sidebar-scroll.tsx | 14 +-
docs/src/router.tsx | 5 +-
docs/src/styles/app.css | 6 +-
ignores.ts | 2 +
pnpm-lock.yaml | 2060 +++++++++++-------------
8 files changed, 959 insertions(+), 1145 deletions(-)
diff --git a/.gitignore b/.gitignore
index e812c3e6..2a45a72e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
node_modules
.DS_Store
.pnpm-store
+.agents
+.claude
diff --git a/docs/package.json b/docs/package.json
index d3e13bd5..f72a28af 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -15,15 +15,17 @@
"start": "node .output/server/index.mjs"
},
"dependencies": {
+ "@base-ui/react": "^1.7.0",
"@remixicon/react": "^4.9.0",
"@tanstack/react-router": "^1.170.8",
"@tanstack/react-router-devtools": "^1.167.0",
"@tanstack/react-start": "^1.168.14",
"@vercel/analytics": "^2.0.1",
- "fumadocs-core": "^16.9.3",
- "fumadocs-mdx": "^15.0.10",
- "fumadocs-twoslash": "^3.2.0",
- "fumadocs-ui": "^16.9.3",
+ "flexsearch": "^0.8.212",
+ "fumadocs-core": "^16.15.4",
+ "fumadocs-mdx": "^15.4.0",
+ "fumadocs-twoslash": "^3.3.0",
+ "fumadocs-ui": "npm:@fumadocs/base-ui@^16.15.4",
"mdast-util-from-markdown": "^2.0.2",
"mermaid": "^11.15.0",
"permix": "workspace:*",
diff --git a/docs/source.config.ts b/docs/source.config.ts
index d87f6c62..f66fd49f 100644
--- a/docs/source.config.ts
+++ b/docs/source.config.ts
@@ -2,6 +2,7 @@ import {
rehypeCodeDefaultOptions,
remarkMdxMermaid,
} from 'fumadocs-core/mdx-plugins'
+import { metaSchema, pageSchema } from 'fumadocs-core/source/schema'
import { defineConfig, defineDocs } from 'fumadocs-mdx/config'
import { transformerTwoslash } from 'fumadocs-twoslash'
import { createFileSystemTypesCache } from 'fumadocs-twoslash/cache-fs'
@@ -11,10 +12,14 @@ import { remarkIncludeChangelog } from './remark-include-changelog'
export const docs = defineDocs({
dir: 'content/docs',
docs: {
+ schema: pageSchema,
postprocess: {
includeProcessedMarkdown: true,
},
},
+ meta: {
+ schema: metaSchema,
+ },
})
export default defineConfig({
diff --git a/docs/src/components/sidebar-scroll.tsx b/docs/src/components/sidebar-scroll.tsx
index 50e8ee06..b1eeb363 100644
--- a/docs/src/components/sidebar-scroll.tsx
+++ b/docs/src/components/sidebar-scroll.tsx
@@ -1,10 +1,18 @@
import { useRouterState } from '@tanstack/react-router'
import { useEffect } from 'react'
-function scrollActiveSidebarItem() {
- const viewport = document.querySelector(
- '#nd-sidebar [data-radix-scroll-area-viewport]'
+function getSidebarViewport() {
+ return document.querySelector(
+ [
+ '#nd-sidebar [data-slot="scroll-area-viewport"]',
+ '#nd-sidebar [data-base-ui-scroll-area-viewport]',
+ '#nd-sidebar',
+ ].join(', ')
)
+}
+
+function scrollActiveSidebarItem() {
+ const viewport = getSidebarViewport()
const active = document.querySelector('#nd-sidebar [data-active="true"]')
if (!viewport || !active) {
diff --git a/docs/src/router.tsx b/docs/src/router.tsx
index 4c3fd8f7..a13539de 100644
--- a/docs/src/router.tsx
+++ b/docs/src/router.tsx
@@ -9,7 +9,10 @@ export function getRouter() {
routeTree,
defaultPreload: 'intent',
scrollRestoration: true,
- scrollToTopSelectors: ['#nd-sidebar [data-radix-scroll-area-viewport]'],
+ scrollToTopSelectors: [
+ '#nd-sidebar [data-slot="scroll-area-viewport"]',
+ '#nd-sidebar [data-base-ui-scroll-area-viewport]',
+ ],
defaultNotFoundComponent: NotFound,
})
}
diff --git a/docs/src/styles/app.css b/docs/src/styles/app.css
index 796799a4..f4e4ebfb 100644
--- a/docs/src/styles/app.css
+++ b/docs/src/styles/app.css
@@ -3,6 +3,9 @@
@import 'fumadocs-ui/css/preset.css';
@import 'fumadocs-twoslash/twoslash.css';
+@source "../**/*.{ts,tsx}";
+@source "../../node_modules/fumadocs-ui/dist/**/*.js";
+
html {
scrollbar-gutter: stable;
}
@@ -12,6 +15,7 @@ html > body[data-scroll-locked] {
--removed-body-scroll-bar-size: 0px !important;
}
-[data-radix-scroll-area-viewport] {
+[data-slot='scroll-area-viewport'],
+[data-base-ui-scroll-area-viewport] {
overflow-y: auto !important;
}
diff --git a/ignores.ts b/ignores.ts
index 9e77439e..65349182 100644
--- a/ignores.ts
+++ b/ignores.ts
@@ -3,6 +3,8 @@ export const ignorePatterns = [
'**/.next/**',
'**/.turbo/**',
'**/.vercel/**',
+ '**/.agents/**',
+ '**/.claude/**',
'**/dist/**',
'**/build/**',
'**/coverage/**',
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 50108e3d..08920078 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -44,6 +44,9 @@ importers:
docs:
dependencies:
+ '@base-ui/react':
+ specifier: ^1.7.0
+ version: 1.7.0(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@remixicon/react':
specifier: ^4.9.0
version: 4.9.0(react@19.2.6)
@@ -55,22 +58,25 @@ importers:
version: 1.167.0(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start':
specifier: ^1.168.14
- version: 1.168.24(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 1.168.24(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vercel/analytics':
specifier: ^2.0.1
- version: 2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@6.0.3))
+ version: 2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@6.0.3))
+ flexsearch:
+ specifier: ^0.8.212
+ version: 0.8.212
fumadocs-core:
- specifier: ^16.9.3
- version: 16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
+ specifier: ^16.15.4
+ version: 16.15.4(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.15)(flexsearch@0.8.212)(lucide-react@1.34.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
fumadocs-mdx:
- specifier: ^15.0.10
- version: 15.0.10(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ specifier: ^15.4.0
+ version: 15.4.0(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.15.4(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.15)(flexsearch@0.8.212)(lucide-react@1.34.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
fumadocs-twoslash:
- specifier: ^3.2.0
- version: 3.2.0(1d030e86b7f7b9132e23a7c3ab6b57d0)
+ specifier: ^3.3.0
+ version: 3.3.0(4805773098752cdf60a478d7cf7e5e6e)
fumadocs-ui:
- specifier: ^16.9.3
- version: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)
+ specifier: npm:@fumadocs/base-ui@^16.15.4
+ version: '@fumadocs/base-ui@16.15.4(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.15.4(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.15)(flexsearch@0.8.212)(lucide-react@1.34.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)'
mdast-util-from-markdown:
specifier: ^2.0.2
version: 2.0.3
@@ -91,14 +97,14 @@ importers:
version: 3.6.0
vite:
specifier: ^8.0.16
- version: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ version: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
devDependencies:
'@orpc/server':
specifier: ^1.14.4
version: 1.14.4(crossws@0.4.5(srvx@0.11.16))(fastify@5.8.5)(ws@8.21.0)
'@tailwindcss/vite':
specifier: ^4.3.0
- version: 4.3.0(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 4.3.0(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@trpc/server':
specifier: ^11.17.0
version: 11.17.0(typescript@6.0.3)
@@ -119,7 +125,7 @@ importers:
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
specifier: ^6.0.2
- version: 6.0.2(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 6.0.2(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
drizzle-orm:
specifier: ^1.0.0-rc.3
version: 1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(effect@3.21.2)(zod@4.4.3)
@@ -137,7 +143,7 @@ importers:
version: 4.12.23
nitro:
specifier: ^3.0.260522-beta
- version: 3.0.260522-beta(chokidar@5.0.0)(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3))(jiti@2.7.0)(lru-cache@11.5.1)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 3.0.260522-beta(chokidar@5.0.0)(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3))(jiti@2.7.0)(lru-cache@11.5.1)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
tailwindcss:
specifier: ^4.3.0
version: 4.3.0
@@ -165,13 +171,13 @@ importers:
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
specifier: ^6.0.2
- version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
typescript:
specifier: ^6.0.3
version: 6.0.3
vite:
specifier: ^8.0.16
- version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
examples/express:
dependencies:
@@ -233,16 +239,16 @@ importers:
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
specifier: ^6.0.2
- version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
tsx:
specifier: ^4.22.4
version: 4.22.4
vite:
specifier: ^8.0.16
- version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
vite-tsconfig-paths:
specifier: ^6.1.1
- version: 6.1.1(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 6.1.1(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
examples/feature-flags:
dependencies:
@@ -264,13 +270,13 @@ importers:
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
specifier: ^6.0.2
- version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
typescript:
specifier: ^6.0.3
version: 6.0.3
vite:
specifier: ^8.0.16
- version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
examples/next:
dependencies:
@@ -326,13 +332,13 @@ importers:
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
specifier: ^6.0.2
- version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
typescript:
specifier: ^6.0.3
version: 6.0.3
vite:
specifier: ^8.0.16
- version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
examples/rebac:
dependencies:
@@ -370,13 +376,13 @@ importers:
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
specifier: ^6.0.2
- version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
typescript:
specifier: ^6.0.3
version: 6.0.3
vite:
specifier: ^8.0.16
- version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
examples/solid:
dependencies:
@@ -392,10 +398,10 @@ importers:
version: 6.0.3
vite:
specifier: ^8.0.16
- version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
vite-plugin-solid:
specifier: ^2.11.12
- version: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
examples/svelte:
dependencies:
@@ -405,13 +411,13 @@ importers:
devDependencies:
'@sveltejs/adapter-auto':
specifier: ^7.0.1
- version: 7.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))
+ version: 7.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))
'@sveltejs/kit':
specifier: ^2.61.1
- version: 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@sveltejs/vite-plugin-svelte':
specifier: ^7.1.2
- version: 7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@types/node':
specifier: ^25.9.1
version: 25.9.1
@@ -426,13 +432,13 @@ importers:
version: 6.0.3
vite:
specifier: ^8.0.16
- version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
examples/tanstack-start:
dependencies:
'@tailwindcss/vite':
specifier: ^4.1.18
- version: 4.3.0(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 4.3.0(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/react-devtools':
specifier: latest
version: 0.10.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(solid-js@1.9.13)
@@ -447,10 +453,10 @@ importers:
version: 1.167.2(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start':
specifier: latest
- version: 1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/router-plugin':
specifier: ^1.132.0
- version: 1.168.13(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 1.168.13(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
lucide-react:
specifier: ^0.545.0
version: 0.545.0(react@19.2.6)
@@ -472,7 +478,7 @@ importers:
version: 0.5.19(tailwindcss@4.3.0)
'@tanstack/devtools-vite':
specifier: latest
- version: 0.8.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 0.8.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/router-cli':
specifier: ^1.132.0
version: 1.167.17
@@ -493,7 +499,7 @@ importers:
version: 19.2.3(@types/react@19.2.15)
'@vitejs/plugin-react':
specifier: ^6.0.1
- version: 6.0.2(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 6.0.2(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
jsdom:
specifier: ^28.1.0
version: 28.1.0(@noble/hashes@1.8.0)
@@ -502,10 +508,10 @@ importers:
version: 6.0.3
vite:
specifier: ^8.0.0
- version: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ version: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
vitest:
specifier: ^4.1.5
- version: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
examples/vue:
dependencies:
@@ -518,7 +524,7 @@ importers:
devDependencies:
'@vitejs/plugin-vue':
specifier: ^6.0.7
- version: 6.0.7(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.35(typescript@6.0.3))
+ version: 6.0.7(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.35(typescript@6.0.3))
'@vue/tsconfig':
specifier: ^0.9.1
version: 0.9.1(typescript@6.0.3)(vue@3.5.35(typescript@6.0.3))
@@ -527,7 +533,7 @@ importers:
version: 6.0.3
vite:
specifier: ^8.0.16
- version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
vue-tsc:
specifier: ^3.3.3
version: 3.3.3(typescript@6.0.3)
@@ -573,13 +579,13 @@ importers:
version: 2.5.7(svelte@5.56.0)(typescript@6.0.3)
'@sveltejs/vite-plugin-svelte':
specifier: ^7.1.2
- version: 7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/intent':
specifier: ^0.0.42
version: 0.0.42
'@tanstack/react-start':
specifier: ^1.168.18
- version: 1.168.18(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 1.168.18(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@testing-library/jest-dom':
specifier: ^6.9.1
version: 6.9.1
@@ -588,7 +594,7 @@ importers:
version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@testing-library/svelte':
specifier: ^5.3.1
- version: 5.3.1(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.8)
+ version: 5.3.1(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.8)
'@types/express':
specifier: ^5.0.6
version: 5.0.6
@@ -603,7 +609,7 @@ importers:
version: 7.2.0
'@vitejs/plugin-react':
specifier: ^6.0.2
- version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vitest/coverage-v8':
specifier: ^4.1.8
version: 4.1.8(vitest@4.1.8)
@@ -639,10 +645,10 @@ importers:
version: 6.0.3
vite-plugin-solid:
specifier: ^2.11.12
- version: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
vitest:
specifier: ^4.1.8
- version: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
vue:
specifier: ^3.5.17
version: 3.5.35(typescript@6.0.3)
@@ -800,6 +806,33 @@ packages:
resolution: {integrity: sha512-p7/ABylAYlexb31wtRdIfH9L9A0Z2T/9H6zAqzqndkY2PLkvNNc580wGhp/gGKN4Sp9sQvSkhc6Oga8/O+wTyw==}
engines: {node: ^22.18.0 || >=24.11.0}
+ '@base-ui/react@1.7.0':
+ resolution: {integrity: sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@date-fns/tz': ^1.2.0
+ '@types/react': ^17 || ^18 || ^19
+ date-fns: ^4.0.0
+ react: ^17 || ^18 || ^19
+ react-dom: ^17 || ^18 || ^19
+ peerDependenciesMeta:
+ '@date-fns/tz':
+ optional: true
+ '@types/react':
+ optional: true
+ date-fns:
+ optional: true
+
+ '@base-ui/utils@0.3.2':
+ resolution: {integrity: sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ==}
+ peerDependencies:
+ '@types/react': ^17 || ^18 || ^19
+ react: ^17 || ^18 || ^19
+ react-dom: ^17 || ^18 || ^19
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@bcoe/v8-coverage@1.0.2':
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
engines: {node: '>=18'}
@@ -957,156 +990,312 @@ packages:
cpu: [ppc64]
os: [aix]
+ '@esbuild/aix-ppc64@0.28.2':
+ resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
'@esbuild/android-arm64@0.28.0':
resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
+ '@esbuild/android-arm64@0.28.2':
+ resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
'@esbuild/android-arm@0.28.0':
resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
+ '@esbuild/android-arm@0.28.2':
+ resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
'@esbuild/android-x64@0.28.0':
resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
+ '@esbuild/android-x64@0.28.2':
+ resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
'@esbuild/darwin-arm64@0.28.0':
resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
+ '@esbuild/darwin-arm64@0.28.2':
+ resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
'@esbuild/darwin-x64@0.28.0':
resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
+ '@esbuild/darwin-x64@0.28.2':
+ resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
'@esbuild/freebsd-arm64@0.28.0':
resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
+ '@esbuild/freebsd-arm64@0.28.2':
+ resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
'@esbuild/freebsd-x64@0.28.0':
resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
+ '@esbuild/freebsd-x64@0.28.2':
+ resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
'@esbuild/linux-arm64@0.28.0':
resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
+ '@esbuild/linux-arm64@0.28.2':
+ resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
'@esbuild/linux-arm@0.28.0':
resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
+ '@esbuild/linux-arm@0.28.2':
+ resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
'@esbuild/linux-ia32@0.28.0':
resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
+ '@esbuild/linux-ia32@0.28.2':
+ resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
'@esbuild/linux-loong64@0.28.0':
resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
+ '@esbuild/linux-loong64@0.28.2':
+ resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
'@esbuild/linux-mips64el@0.28.0':
resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
+ '@esbuild/linux-mips64el@0.28.2':
+ resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
'@esbuild/linux-ppc64@0.28.0':
resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
+ '@esbuild/linux-ppc64@0.28.2':
+ resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
'@esbuild/linux-riscv64@0.28.0':
resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
+ '@esbuild/linux-riscv64@0.28.2':
+ resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
'@esbuild/linux-s390x@0.28.0':
resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
+ '@esbuild/linux-s390x@0.28.2':
+ resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
'@esbuild/linux-x64@0.28.0':
resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
+ '@esbuild/linux-x64@0.28.2':
+ resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
'@esbuild/netbsd-arm64@0.28.0':
resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
+ '@esbuild/netbsd-arm64@0.28.2':
+ resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
'@esbuild/netbsd-x64@0.28.0':
resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
+ '@esbuild/netbsd-x64@0.28.2':
+ resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
'@esbuild/openbsd-arm64@0.28.0':
resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
+ '@esbuild/openbsd-arm64@0.28.2':
+ resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
'@esbuild/openbsd-x64@0.28.0':
resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
+ '@esbuild/openbsd-x64@0.28.2':
+ resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
'@esbuild/openharmony-arm64@0.28.0':
resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
+ '@esbuild/openharmony-arm64@0.28.2':
+ resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
'@esbuild/sunos-x64@0.28.0':
resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
+ '@esbuild/sunos-x64@0.28.2':
+ resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
'@esbuild/win32-arm64@0.28.0':
resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
+ '@esbuild/win32-arm64@0.28.2':
+ resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
'@esbuild/win32-ia32@0.28.0':
resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
+ '@esbuild/win32-ia32@0.28.2':
+ resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
'@esbuild/win32-x64@0.28.0':
resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
+ '@esbuild/win32-x64@0.28.2':
+ resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
'@exodus/bytes@1.15.1':
resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
@@ -1134,32 +1323,62 @@ packages:
'@fastify/proxy-addr@5.1.0':
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
- '@floating-ui/core@1.7.5':
- resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
+ '@floating-ui/core@1.8.0':
+ resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==}
- '@floating-ui/dom@1.7.6':
- resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==}
+ '@floating-ui/dom@1.8.0':
+ resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==}
- '@floating-ui/react-dom@2.1.8':
- resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==}
+ '@floating-ui/react-dom@2.1.9':
+ resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
- '@floating-ui/utils@0.2.11':
- resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
+ '@floating-ui/utils@0.2.12':
+ resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==}
- '@fumadocs/tailwind@0.0.5':
- resolution: {integrity: sha512-ENKPWUDRmriccsrUDE4bDBq3FNr/ms3BP2rWlsAEMV1yP23pcCaan+ceGfeBUsAQjw7sj9Q3R4Kl3g/TCStPzQ==}
+ '@fuma-translate/react@1.0.2':
+ resolution: {integrity: sha512-uOiOtBx3nRXR8Nu1GzBf1tApgF1FErDBTHxRIAQeyQdyOoZbrNRN6H4kDCWObY4qyGeGbHydG0DHzgeUgFDMIw==}
peerDependencies:
- '@tailwindcss/oxide': ^4.0.0
- tailwindcss: ^4.0.0
+ '@types/react': '*'
+ react: ^19.2.0
+ react-dom: ^19.2.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@fumadocs/base-ui@16.15.4':
+ resolution: {integrity: sha512-BcoUPoSbfX/uaUl2NgPn0IKOjqJCkwsWPP6z5uRlKSEdh4zbImwXf3k6+qJ3ULlVaFEY8hqfR2wn+Ya5vPKKEQ==}
+ peerDependencies:
+ '@types/mdx': '*'
+ '@types/react': '*'
+ fumadocs-core: 16.15.4
+ next: 16.x.x
+ react: ^19.2.0
+ react-dom: ^19.2.0
+ takumi-js: '*'
peerDependenciesMeta:
- '@tailwindcss/oxide':
+ '@types/mdx':
+ optional: true
+ '@types/react':
+ optional: true
+ next:
+ optional: true
+ takumi-js:
optional: true
+
+ '@fumadocs/tailwind@0.1.1':
+ resolution: {integrity: sha512-BnPe52UxSaG8yKlHMKBxXw8h6GpK5qO55ci6+Qd5JnquTvIw6SpfbC1P+qAi82PuPWv1KZAWY8bxRk4+x9ctXw==}
+ peerDependencies:
+ tailwindcss: ^4.0.0
+ peerDependenciesMeta:
tailwindcss:
optional: true
+ '@fumari/image-size@0.1.0':
+ resolution: {integrity: sha512-x2o9u6P8uKUK15B8XgEoRhR3PgLoLSbQKK6FUCd14JzumEw+e8FXZPelij/dZ4VQVMp4r61VD3DcvSo3aEhLAA==}
+
'@henrygd/queue@1.2.0':
resolution: {integrity: sha512-jW/BLSTpcvExDhqJGxtIPgGr2O0IFF8XUNDwEbfCfhrXT8a4xztQ9Lv6U/vbYzYC0xVWn+3zv6YnLUh3bEFUKA==}
@@ -1453,10 +1672,6 @@ packages:
resolution: {integrity: sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==}
engines: {node: '>=20.0'}
- '@orama/orama@3.1.18':
- resolution: {integrity: sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA==}
- engines: {node: '>= 20.0.0'}
-
'@orpc/client@1.14.4':
resolution: {integrity: sha512-i6Z9FikIm9Qz3Br10vk8/cllgjdYdlRKK2OV3x2/CdOBRr+B68tboNdpH3eeb1kv8/Zd6ZsXcWaDfrANdj2GZQ==}
@@ -1931,371 +2146,6 @@ packages:
'@quansync/fs@1.0.0':
resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==}
- '@radix-ui/number@1.1.1':
- resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
-
- '@radix-ui/primitive@1.1.3':
- resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
-
- '@radix-ui/react-accordion@1.2.12':
- resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-arrow@1.1.7':
- resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-collapsible@1.1.12':
- resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-collection@1.1.7':
- resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-compose-refs@1.1.2':
- resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-context@1.1.2':
- resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-dialog@1.1.15':
- resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-direction@1.1.1':
- resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-dismissable-layer@1.1.11':
- resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-focus-guards@1.1.3':
- resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-focus-scope@1.1.7':
- resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-id@1.1.1':
- resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-navigation-menu@1.2.14':
- resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-popover@1.1.15':
- resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-popper@1.2.8':
- resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-portal@1.1.9':
- resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-presence@1.1.5':
- resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-primitive@2.1.3':
- resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-roving-focus@1.1.11':
- resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-scroll-area@1.2.10':
- resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-slot@1.2.3':
- resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-slot@1.2.4':
- resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-tabs@1.1.13':
- resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/react-use-callback-ref@1.1.1':
- resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-use-controllable-state@1.2.2':
- resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-use-effect-event@0.0.2':
- resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-use-escape-keydown@1.1.1':
- resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-use-layout-effect@1.1.1':
- resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-use-previous@1.1.1':
- resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-use-rect@1.1.1':
- resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-use-size@1.1.1':
- resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==}
- peerDependencies:
- '@types/react': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-visually-hidden@1.2.3':
- resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
- peerDependencies:
- '@types/react': '*'
- '@types/react-dom': '*'
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
- '@radix-ui/rect@1.1.1':
- resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
-
'@remixicon/react@4.9.0':
resolution: {integrity: sha512-5/jLDD4DtKxH2B4QVXTobvV1C2uL8ab9D5yAYNtFt+w80O0Ys1xFOrspqROL3fjrZi+7ElFUWE37hBfaAl6U+Q==}
peerDependencies:
@@ -2402,38 +2252,38 @@ packages:
'@sec-ant/readable-stream@0.4.1':
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
- '@shikijs/core@4.1.0':
- resolution: {integrity: sha512-jLJtSJeuFffqX6/inRE1zqU5aFv2hrszvYgq3OjbAgFRZiWv7abKMDdQzYxuSDfmUPQozZvI/kuy6VMTvnvqTQ==}
+ '@shikijs/core@4.4.3':
+ resolution: {integrity: sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==}
engines: {node: '>=20'}
- '@shikijs/engine-javascript@4.1.0':
- resolution: {integrity: sha512-YquhawCUgaBfhsS72e2Y/dI59gCBNPHu3fEO/tvLaXrTssxZrY5ddjtNLTwndrMgPo8b3IscE+xoICDzpTmlFQ==}
+ '@shikijs/engine-javascript@4.4.3':
+ resolution: {integrity: sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==}
engines: {node: '>=20'}
- '@shikijs/engine-oniguruma@4.1.0':
- resolution: {integrity: sha512-axLpjVs45YBvvINa+dJF+NPW+KtFkNXsFr4SDw2BMj9GdeMnGxVB9PQb2xXlJYovslt/nz6giedAyOANkfc7hg==}
+ '@shikijs/engine-oniguruma@4.4.3':
+ resolution: {integrity: sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==}
engines: {node: '>=20'}
- '@shikijs/langs@4.1.0':
- resolution: {integrity: sha512-nwOMruEkbgdZfQ/b8CgpNBVOpvG1k0N5tbmgiFeqsan401+x3ILqlzZJowSla4Agmq4hG2Uf2wh5jLTEhR8VSg==}
+ '@shikijs/langs@4.4.3':
+ resolution: {integrity: sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==}
engines: {node: '>=20'}
- '@shikijs/primitive@4.1.0':
- resolution: {integrity: sha512-zx2/2Uwj2q9X3KSyYREEhXO23xBw5WUhP4orK2lE4r+t9JGITmEe0JH+wPmJhqHpOT2bRRs6lAL945+LDvOAGw==}
+ '@shikijs/primitive@4.4.3':
+ resolution: {integrity: sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==}
engines: {node: '>=20'}
- '@shikijs/themes@4.1.0':
- resolution: {integrity: sha512-emCcTnUM7yO2wltYbaxm+yLvcCI4+h8XBKc4KmJ7EZUXoSGjcCHifkI//R4OFit9ewpg7H2/9tjOuXrT2v/Knw==}
+ '@shikijs/themes@4.4.3':
+ resolution: {integrity: sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==}
engines: {node: '>=20'}
- '@shikijs/twoslash@4.1.0':
- resolution: {integrity: sha512-XD7d3LqLXOaL6PbFKGwtdnfcyBdOfKWfxb1+c4MVknVTemwnMQxqj1wYhhWBLRdk8H3Fp9pyf/FILfy2Nzeg8g==}
+ '@shikijs/twoslash@4.4.3':
+ resolution: {integrity: sha512-m7HNzunEIHRk1jCya3ngGsO3+8pYxrPIIxtdJewg/W8ceW/+m/mSsm4jM3L9DvYYNa8Rvbu7Dabt3BOpCclz8Q==}
engines: {node: '>=20'}
peerDependencies:
typescript: '>=5.5.0'
- '@shikijs/types@4.1.0':
- resolution: {integrity: sha512-3EQWX54fMpniOrDblzAhiwiJwpiTMW6+B9DWyUd9ska483tbayFYuw47UxwuPknI31bKnySfVQ/QW+jFL4rFdA==}
+ '@shikijs/types@4.4.3':
+ resolution: {integrity: sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==}
engines: {node: '>=20'}
'@shikijs/vscode-textmate@10.0.2':
@@ -3368,6 +3218,9 @@ packages:
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+ '@types/hast@3.0.5':
+ resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==}
+
'@types/http-errors@2.0.5':
resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
@@ -3599,6 +3452,75 @@ packages:
vue:
optional: true
+ '@yuku-analyzer/binding-android-arm64@0.9.3':
+ resolution: {integrity: sha512-6dOwkawiJYtUUBchfjrFo7poz460Yg+aQNgr4lHUPZ2fNW+llpMEZkbznTq25BEmmiBGPJr+L15dzTvR8zP4vQ==}
+ cpu: [arm64]
+ os: [android]
+
+ '@yuku-analyzer/binding-darwin-arm64@0.9.3':
+ resolution: {integrity: sha512-DTRoWK7AqNfshN+DcCS/s4n86br0ZusISeXLZWWNuvnZ3b9LCVXdWRVPlnMpEwsmSH3c3QSwL7MAOPyEHMe+FA==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@yuku-analyzer/binding-darwin-x64@0.9.3':
+ resolution: {integrity: sha512-EWzaR0/AL3ikZfUiADG1SmbB+wF7GGhXFKhMWm3RbOTf+ATyEW7Fa9nsjo1K70pWEeVZHRm9MU4iq7LIUEEgfg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@yuku-analyzer/binding-freebsd-x64@0.9.3':
+ resolution: {integrity: sha512-Ao4/v+ppzIFVs2SldvBM2hc9NsijXNsKc1xUY+d+Nv20HrjRdm96HtrkIViw7zI5i6Ca77jkd8VbhWE8E4kgyg==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@yuku-analyzer/binding-linux-arm-gnu@0.9.3':
+ resolution: {integrity: sha512-EZc2H6bAyl4u3z48Jtqf4Un1657TpOkBWEmSdPCY1tHCO4YUFaNAz4dPA93yWgo5t38oRQT9UoXlwse/4aLtxw==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@yuku-analyzer/binding-linux-arm-musl@0.9.3':
+ resolution: {integrity: sha512-pKny3wEa2Xl4kPoqNU8fO5NCzcp9VgMAkLpN3GZ5TOkdz23ppuzUqddlhad43/9puxfckX5aZqfxML/1vGoxBw==}
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+
+ '@yuku-analyzer/binding-linux-arm64-gnu@0.9.3':
+ resolution: {integrity: sha512-bfpsIupfbX7N4ueSjo3Zm+Mob0q7MjrZX+INMGcPjNnXBcyKjkzrTotkqbEbwgfiNKhqyDVjynwsh64xsv5IPg==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@yuku-analyzer/binding-linux-arm64-musl@0.9.3':
+ resolution: {integrity: sha512-Q+AAUrrsgYgHb0/Wrm+HnSKN7xOpkC+6Y2812dZw5/rrSX7+qjRjJ+3uNnCZlkHpH3Hy7WXbLIa/IncP4bcHQA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@yuku-analyzer/binding-linux-x64-gnu@0.9.3':
+ resolution: {integrity: sha512-ZQyjmSDRHTkDlddrzmIG1/nMUYDZC21XUguzQ4qvSWtfq6CsfJvIDFBPfJ03YDNQ+wR0px9Ly/xAa8VC0p9rHQ==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@yuku-analyzer/binding-linux-x64-musl@0.9.3':
+ resolution: {integrity: sha512-EIYzThB5pI3BiZHFNYyY8nMQ38z9l8/kT8uYvfYVpZ9TNEC0YgqX95MH61l9RILCYFDx+DbLoGajGY55JFvb6Q==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@yuku-analyzer/binding-win32-arm64@0.9.3':
+ resolution: {integrity: sha512-KF+Ho3jtjikfPYJ76NL1KiPYwF+0UjnsASX0k461BZ1Er4pRQG39o8WvsngLT+fDg6JxB3v5J+ScDrto29RVnA==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@yuku-analyzer/binding-win32-x64@0.9.3':
+ resolution: {integrity: sha512-4pdUfYVYPf05vyygvWiXsiI/tdPqn5bBiHs7c1TBIm3Kx7/w5pq++myNQ/8CUH/Proz+1kHtCr/lDJF9k67fqg==}
+ cpu: [x64]
+ os: [win32]
+
+ '@yuku-toolchain/types@0.9.3':
+ resolution: {integrity: sha512-rFE+5P4g2wxko5C85MugJOlVjBHEQq87dIkhLkniLXLp63PEtgaFjD954i5HXlfnyzLxPcZHsSOVDVgmo1HToA==}
+
abbrev@2.0.0:
resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
@@ -3669,10 +3591,6 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
- aria-hidden@1.2.6:
- resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
- engines: {node: '>=10'}
-
aria-query@5.3.0:
resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
@@ -3852,6 +3770,14 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
+ cnfast@0.0.8:
+ resolution: {integrity: sha512-EjXKMfGfdwtV4AcNSQ6AwQaVzpC1B7IxeiwA3FlhTXz+YFlMKVi4c1JX9tgD2QOlahQXjB8KUXrBaYG+3v871Q==}
+ hasBin: true
+
+ cnfast@0.1.0:
+ resolution: {integrity: sha512-rH0jBKeLkVrK7NsZ5Ba2l7WdMBmm1k0FMpABeXUU1PgTUxbz3261gEuCSsrYuXo4BwAe3yCcIbQ93YyS52lOGQ==}
+ hasBin: true
+
collapse-white-space@2.1.0:
resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==}
@@ -4524,6 +4450,11 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ esbuild@0.28.2:
+ resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
@@ -4682,6 +4613,9 @@ packages:
find-workspaces@0.3.1:
resolution: {integrity: sha512-UDkGILGJSA1LN5Aa7McxCid4sqW3/e+UYsVwyxki3dDT0F8+ym0rAfnCkEfkL0rO7M+8/mvkim4t/s3IPHmg+w==}
+ flexsearch@0.8.212:
+ resolution: {integrity: sha512-wSyJr1GUWoOOIISRu+X2IXiOcVfg9qqBRyCPRUdLMIGJqPzMo+jMRlvE83t14v1j0dRMEaBbER/adQjp6Du2pw==}
+
foreground-child@3.3.1:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'}
@@ -4698,15 +4632,12 @@ packages:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'}
- framer-motion@12.40.0:
- resolution: {integrity: sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==}
+ framer-motion@13.1.1:
+ resolution: {integrity: sha512-B/xn2TPS4f61cEBLFjiYlQFnBZUW1YVj/LM+C+N4OP8Rs95VLEI2ot/RlfBg111la/EiyECFaJJi/A3FWA8MUA==}
peerDependencies:
- '@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
peerDependenciesMeta:
- '@emotion/is-prop-valid':
- optional: true
react:
optional: true
react-dom:
@@ -4721,8 +4652,8 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
- fumadocs-core@16.9.3:
- resolution: {integrity: sha512-8RVzKnzBJR5o+tJCccY28ntekfMQYBoYiz7alnYb/d9YJc+XpnsINzTl63lQ1eBMZ9gdhm2MqRtgUjh/8rUrbw==}
+ fumadocs-core@16.15.4:
+ resolution: {integrity: sha512-kdOuM0tvHkLWajnDu73BmtryPuUq4xsu610ssl1YMAm9fYF7BRmvYxx2apHbyf2Lt+7w8OEiWDTmB8Ja3zqp4Q==}
peerDependencies:
'@mdx-js/mdx': '*'
'@mixedbread/sdk': 0.x.x
@@ -4739,7 +4670,7 @@ packages:
next: 16.x.x
react: ^19.2.0
react-dom: ^19.2.0
- react-router: 7.x.x
+ react-router: 7.x.x || 8.x.x
waku: '*'
zod: 4.x.x
peerDependenciesMeta:
@@ -4780,20 +4711,24 @@ packages:
zod:
optional: true
- fumadocs-mdx@15.0.10:
- resolution: {integrity: sha512-kH3S7ESS9yXTAaCkA8dDugsCK/MbnpgyZ5qBEL7cWoavV0O/T4+4YTYFkvNknz7cw+T/r+OG0p2BvlVhkk4fww==}
+ fumadocs-mdx@15.4.0:
+ resolution: {integrity: sha512-bJCQfsckKUQ4x2pyz8OdASEAARVMB49kOej7njaJ3t+tYsP1HnwkmqiO8e/jdQHcv6JH3XkXWidTho3ZC/WBcA==}
hasBin: true
peerDependencies:
+ '@fumadocs/satteri': 0.x.x
'@types/mdast': '*'
'@types/mdx': '*'
'@types/react': '*'
- fumadocs-core: ^16.7.0
+ fumadocs-core: ^16.15.3
mdast-util-directive: '*'
next: ^15.3.0 || ^16.0.0
react: ^19.2.0
rolldown: '*'
+ satteri: ^0.10.5
vite: 7.x.x || 8.x.x
peerDependenciesMeta:
+ '@fumadocs/satteri':
+ optional: true
'@types/mdast':
optional: true
'@types/mdx':
@@ -4808,11 +4743,13 @@ packages:
optional: true
rolldown:
optional: true
+ satteri:
+ optional: true
vite:
optional: true
- fumadocs-twoslash@3.2.0:
- resolution: {integrity: sha512-hzoxc2HR9dw2z5T1NNLYCbMY1DFiSF71+X2KJS4t/BalRyiBxpCcP9zAyzofba07YkrGhZ6xW3B105EcpNI2Vw==}
+ fumadocs-twoslash@3.3.0:
+ resolution: {integrity: sha512-IR+oYbjsR59h/R7AvdeEdHEY48ygCHII3tUZGC3QnUVug5+qN1wlRNr30gAfOvrau/mdiRKwONuJCOKOWqEFbg==}
peerDependencies:
'@types/react': '*'
fumadocs-core: ^16.7.16
@@ -4824,26 +4761,6 @@ packages:
'@types/react':
optional: true
- fumadocs-ui@16.9.3:
- resolution: {integrity: sha512-eoVKj1H+ATut0su+WIoPWBLRqzPMGD0hekIBr4GopWvUg1lS997HL4kP+Leyf+3CYlZtFgyXb6ylbvRLFtEj6Q==}
- peerDependencies:
- '@takumi-rs/image-response': '*'
- '@types/mdx': '*'
- '@types/react': '*'
- fumadocs-core: 16.9.3
- next: 16.x.x
- react: ^19.2.0
- react-dom: ^19.2.0
- peerDependenciesMeta:
- '@takumi-rs/image-response':
- optional: true
- '@types/mdx':
- optional: true
- '@types/react':
- optional: true
- next:
- optional: true
-
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
@@ -5366,8 +5283,8 @@ packages:
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
- lucide-react@1.17.0:
- resolution: {integrity: sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w==}
+ lucide-react@1.34.0:
+ resolution: {integrity: sha512-vnjGJNI7Htk5+oWW8gXGuaLgwgAb0T6/iZbBrp9JCfRFwdNWZ0YTm3eyxjOLgwN6r8iyAf3UA70zNmBRBNv7yg==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
@@ -5378,6 +5295,9 @@ packages:
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+ magic-string@1.2.3:
+ resolution: {integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==}
+
magicast@0.5.3:
resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==}
@@ -5641,21 +5561,18 @@ packages:
mlly@1.8.2:
resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==}
- motion-dom@12.40.0:
- resolution: {integrity: sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==}
+ motion-dom@13.1.1:
+ resolution: {integrity: sha512-XSf8VYWSB6G/0IY3rWVbyLcxWXtAVHkN1PQE2agTaCv3u8RGvbwu56TyyR/MNzBqqNavEBTZzErcxI1TxBrjcA==}
- motion-utils@12.39.0:
- resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==}
+ motion-utils@13.0.0:
+ resolution: {integrity: sha512-7DnN7TmbLcYXcG4RVadXIihWlyuM9afoUww8Y5Agg431kGKiuL2/OMyP4mJ5wLz+pvN3t5ySClLOaVXJ+wekRQ==}
- motion@12.40.0:
- resolution: {integrity: sha512-yjrHUrBFW6kQvjJwRsoiPSAhC5tRwRqNGJWmiJ4CrGnbKp0V88AdzkhBmDoqIsIPfarOe0Uddd37Xq43/gIocA==}
+ motion@13.1.1:
+ resolution: {integrity: sha512-WNZoK6xiF+kkTqkZ5K7FDDh6A8BG4i5Hc7KXtW8gtTxkpJFds+hIOrDaQGKjQj/AE/i4hJqAaUHEqp/Qo02y6Q==}
peerDependencies:
- '@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
peerDependenciesMeta:
- '@emotion/is-prop-valid':
- optional: true
react:
optional: true
react-dom:
@@ -5770,6 +5687,10 @@ packages:
resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==}
engines: {node: '>=18'}
+ npm-to-yarn@3.2.0:
+ resolution: {integrity: sha512-K1HmQeZT2HrjpsR6KgqbN2FAXL2NrJJmNUSD9ck7HGTVu1JKXox8n9SB+tjbU8m8JGLF4OscrroPepew/L7/Xw==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
nypm@0.6.9:
resolution: {integrity: sha512-zxlE2yvSWZWmHcNdT3+5zV2lrCogeE9YOklHrR3dFjqutq5wO7GFDYLFDRXLsYnJzwvy/im9fYoxePvS0VTW0w==}
engines: {node: '>=18'}
@@ -6182,6 +6103,9 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
+ reselect@5.3.0:
+ resolution: {integrity: sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==}
+
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -6348,8 +6272,8 @@ packages:
resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==}
engines: {node: '>= 0.4'}
- shiki@4.1.0:
- resolution: {integrity: sha512-l/ABZPUR5v70jI10EzqfMS/I96vjSGv2y0ihUV+WYFzv0EfvW4s54m0Lg8wCrrL+2IkwBzFTuxkZjPf8b2NX9Q==}
+ shiki@4.4.3:
+ resolution: {integrity: sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==}
engines: {node: '>=20'}
side-channel-list@1.0.1:
@@ -6668,11 +6592,11 @@ packages:
resolution: {integrity: sha512-NqgRQy6j6dPYcdSdv0q1g9QsZg7SWg87RERM8otw/1AtKU2yTFVClOM7cbwKzOonZr/Ek1blTBucw64L9H0Bwg==}
hasBin: true
- twoslash-protocol@0.3.8:
- resolution: {integrity: sha512-HmvAHoiEviK8LqvAQyc9/irkdvwTUiR1fHmNwH/0gq8EHxyBt4PWVPixjEXg6wJu1u6yBrILEWXGK9Kw58/8yQ==}
+ twoslash-protocol@0.3.9:
+ resolution: {integrity: sha512-9/iwp+CXOnjFMPQuPL5PkuRbZnDoNpBvtJCLs9t8kDYkL3YHujbvnHfZA1i5fApDftVEdBw+T/4F+dH5kIzpYQ==}
- twoslash@0.3.8:
- resolution: {integrity: sha512-OeDz0kDl8sqPUN3nr7gqcvOs70f5lZsdhKYTX3/SgB9OvdadzzoYJI/4SBXhXV1HG8E9fLc+e17itoRYTxmoig==}
+ twoslash@0.3.9:
+ resolution: {integrity: sha512-rDclk+OtzuTX+tnea7DYLCkqGQ3eP0IyfD+kzUJ7t46X/NzlaxwrhecmEBNuSCuEn3V+n1PhcjUUQQ7gUJzX5Q==}
peerDependencies:
typescript: ^5.5.0 || ^6.0.0
@@ -7135,6 +7059,16 @@ packages:
resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==}
engines: {node: '>=18'}
+ yuku-analyzer@0.9.3:
+ resolution: {integrity: sha512-2xSREErroEF8boH7XyKfVO5hbjP6PCIYYnLR2Abg6PGJaFVbETLucmbzIebBOHeyY4GO6VXzloTuhUldR/zyew==}
+
+ yuku-ast@0.9.3:
+ resolution: {integrity: sha512-Kt0PXlPCXdKF1fWFTl7N3DaxsVk6DHF8VAXHJMkwPtJnWYgbx20pYgGSweuC/86mIM7k1NUITQ4JffgOLUWTCw==}
+
+ zbsearch@4.0.0:
+ resolution: {integrity: sha512-gm4zfO31n2ZdruTTRQoWVWO4Q2+zrDt2GlrvIc+5JulRQNAm4IanCxER82vQ7Ug96FYkGqWUJBXFe1kGAcDWaQ==}
+ engines: {node: '>= 20.0.0'}
+
zimmerframe@1.1.4:
resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==}
@@ -7330,6 +7264,29 @@ snapshots:
'@babel/helper-string-parser': 8.0.0-rc.6
'@babel/helper-validator-identifier': 8.0.0-rc.6
+ '@base-ui/react@1.7.0(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
+ dependencies:
+ '@babel/runtime': 7.29.7
+ '@base-ui/utils': 0.3.2(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@floating-ui/react-dom': 2.1.9(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@floating-ui/utils': 0.2.12
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ use-sync-external-store: 1.6.0(react@19.2.6)
+ optionalDependencies:
+ '@types/react': 19.2.15
+
+ '@base-ui/utils@0.3.2(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
+ dependencies:
+ '@babel/runtime': 7.29.7
+ '@floating-ui/utils': 0.2.12
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ reselect: 5.3.0
+ use-sync-external-store: 1.6.0(react@19.2.6)
+ optionalDependencies:
+ '@types/react': 19.2.15
+
'@bcoe/v8-coverage@1.0.2': {}
'@borewit/text-codec@0.2.2': {}
@@ -7515,81 +7472,159 @@ snapshots:
'@esbuild/aix-ppc64@0.28.0':
optional: true
+ '@esbuild/aix-ppc64@0.28.2':
+ optional: true
+
'@esbuild/android-arm64@0.28.0':
optional: true
+ '@esbuild/android-arm64@0.28.2':
+ optional: true
+
'@esbuild/android-arm@0.28.0':
optional: true
+ '@esbuild/android-arm@0.28.2':
+ optional: true
+
'@esbuild/android-x64@0.28.0':
optional: true
+ '@esbuild/android-x64@0.28.2':
+ optional: true
+
'@esbuild/darwin-arm64@0.28.0':
optional: true
+ '@esbuild/darwin-arm64@0.28.2':
+ optional: true
+
'@esbuild/darwin-x64@0.28.0':
optional: true
+ '@esbuild/darwin-x64@0.28.2':
+ optional: true
+
'@esbuild/freebsd-arm64@0.28.0':
optional: true
+ '@esbuild/freebsd-arm64@0.28.2':
+ optional: true
+
'@esbuild/freebsd-x64@0.28.0':
optional: true
+ '@esbuild/freebsd-x64@0.28.2':
+ optional: true
+
'@esbuild/linux-arm64@0.28.0':
optional: true
+ '@esbuild/linux-arm64@0.28.2':
+ optional: true
+
'@esbuild/linux-arm@0.28.0':
optional: true
+ '@esbuild/linux-arm@0.28.2':
+ optional: true
+
'@esbuild/linux-ia32@0.28.0':
optional: true
+ '@esbuild/linux-ia32@0.28.2':
+ optional: true
+
'@esbuild/linux-loong64@0.28.0':
optional: true
+ '@esbuild/linux-loong64@0.28.2':
+ optional: true
+
'@esbuild/linux-mips64el@0.28.0':
optional: true
+ '@esbuild/linux-mips64el@0.28.2':
+ optional: true
+
'@esbuild/linux-ppc64@0.28.0':
optional: true
+ '@esbuild/linux-ppc64@0.28.2':
+ optional: true
+
'@esbuild/linux-riscv64@0.28.0':
optional: true
+ '@esbuild/linux-riscv64@0.28.2':
+ optional: true
+
'@esbuild/linux-s390x@0.28.0':
optional: true
+ '@esbuild/linux-s390x@0.28.2':
+ optional: true
+
'@esbuild/linux-x64@0.28.0':
optional: true
+ '@esbuild/linux-x64@0.28.2':
+ optional: true
+
'@esbuild/netbsd-arm64@0.28.0':
optional: true
+ '@esbuild/netbsd-arm64@0.28.2':
+ optional: true
+
'@esbuild/netbsd-x64@0.28.0':
optional: true
+ '@esbuild/netbsd-x64@0.28.2':
+ optional: true
+
'@esbuild/openbsd-arm64@0.28.0':
optional: true
+ '@esbuild/openbsd-arm64@0.28.2':
+ optional: true
+
'@esbuild/openbsd-x64@0.28.0':
optional: true
+ '@esbuild/openbsd-x64@0.28.2':
+ optional: true
+
'@esbuild/openharmony-arm64@0.28.0':
optional: true
+ '@esbuild/openharmony-arm64@0.28.2':
+ optional: true
+
'@esbuild/sunos-x64@0.28.0':
optional: true
+ '@esbuild/sunos-x64@0.28.2':
+ optional: true
+
'@esbuild/win32-arm64@0.28.0':
optional: true
+ '@esbuild/win32-arm64@0.28.2':
+ optional: true
+
'@esbuild/win32-ia32@0.28.0':
optional: true
+ '@esbuild/win32-ia32@0.28.2':
+ optional: true
+
'@esbuild/win32-x64@0.28.0':
optional: true
+ '@esbuild/win32-x64@0.28.2':
+ optional: true
+
'@exodus/bytes@1.15.1(@noble/hashes@1.8.0)':
optionalDependencies:
'@noble/hashes': 1.8.0
@@ -7617,28 +7652,63 @@ snapshots:
'@fastify/forwarded': 3.0.1
ipaddr.js: 2.4.0
- '@floating-ui/core@1.7.5':
+ '@floating-ui/core@1.8.0':
dependencies:
- '@floating-ui/utils': 0.2.11
+ '@floating-ui/utils': 0.2.12
- '@floating-ui/dom@1.7.6':
+ '@floating-ui/dom@1.8.0':
dependencies:
- '@floating-ui/core': 1.7.5
- '@floating-ui/utils': 0.2.11
+ '@floating-ui/core': 1.8.0
+ '@floating-ui/utils': 0.2.12
- '@floating-ui/react-dom@2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
+ '@floating-ui/react-dom@2.1.9(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
- '@floating-ui/dom': 1.7.6
+ '@floating-ui/dom': 1.8.0
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- '@floating-ui/utils@0.2.11': {}
+ '@floating-ui/utils@0.2.12': {}
- '@fumadocs/tailwind@0.0.5(@tailwindcss/oxide@4.3.0)(tailwindcss@4.3.0)':
+ '@fuma-translate/react@1.0.2(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
+ dependencies:
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ optionalDependencies:
+ '@types/react': 19.2.15
+
+ '@fumadocs/base-ui@16.15.4(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.15.4(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.15)(flexsearch@0.8.212)(lucide-react@1.34.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)':
+ dependencies:
+ '@base-ui/react': 1.7.0(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@fuma-translate/react': 1.0.2(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@fumadocs/tailwind': 0.1.1(tailwindcss@4.3.0)
+ class-variance-authority: 0.7.1
+ cnfast: 0.1.0
+ fumadocs-core: 16.15.4(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.15)(flexsearch@0.8.212)(lucide-react@1.34.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
+ lucide-react: 1.34.0(react@19.2.6)
+ motion: 13.1.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ next-themes: 0.4.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ react-remove-scroll: 2.7.2(@types/react@19.2.15)(react@19.2.6)
+ rehype-raw: 7.0.0
+ scroll-into-view-if-needed: 3.1.0
+ shiki: 4.4.3
+ unist-util-visit: 5.1.0
+ optionalDependencies:
+ '@types/mdx': 2.0.13
+ '@types/react': 19.2.15
+ next: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ transitivePeerDependencies:
+ - '@date-fns/tz'
+ - date-fns
+ - tailwindcss
+
+ '@fumadocs/tailwind@0.1.1(tailwindcss@4.3.0)':
optionalDependencies:
- '@tailwindcss/oxide': 4.3.0
tailwindcss: 4.3.0
+ '@fumari/image-size@0.1.0': {}
+
'@henrygd/queue@1.2.0': {}
'@iconify/types@2.0.0': {}
@@ -7881,8 +7951,6 @@ snapshots:
'@oozcitak/util@10.0.0': {}
- '@orama/orama@3.1.18': {}
-
'@orpc/client@1.14.4':
dependencies:
'@orpc/shared': 1.14.4
@@ -8111,441 +8179,85 @@ snapshots:
optional: true
'@oxlint-tsgolint/linux-x64@7.0.2001':
- optional: true
-
- '@oxlint-tsgolint/win32-arm64@7.0.2001':
- optional: true
-
- '@oxlint-tsgolint/win32-x64@7.0.2001':
- optional: true
-
- '@oxlint/binding-android-arm-eabi@1.80.0':
- optional: true
-
- '@oxlint/binding-android-arm64@1.80.0':
- optional: true
-
- '@oxlint/binding-darwin-arm64@1.80.0':
- optional: true
-
- '@oxlint/binding-darwin-x64@1.80.0':
- optional: true
-
- '@oxlint/binding-freebsd-x64@1.80.0':
- optional: true
-
- '@oxlint/binding-linux-arm-gnueabihf@1.80.0':
- optional: true
-
- '@oxlint/binding-linux-arm-musleabihf@1.80.0':
- optional: true
-
- '@oxlint/binding-linux-arm64-gnu@1.80.0':
- optional: true
-
- '@oxlint/binding-linux-arm64-musl@1.80.0':
- optional: true
-
- '@oxlint/binding-linux-ppc64-gnu@1.80.0':
- optional: true
-
- '@oxlint/binding-linux-riscv64-gnu@1.80.0':
- optional: true
-
- '@oxlint/binding-linux-riscv64-musl@1.80.0':
- optional: true
-
- '@oxlint/binding-linux-s390x-gnu@1.80.0':
- optional: true
-
- '@oxlint/binding-linux-x64-gnu@1.80.0':
- optional: true
-
- '@oxlint/binding-linux-x64-musl@1.80.0':
- optional: true
-
- '@oxlint/binding-openharmony-arm64@1.80.0':
- optional: true
-
- '@oxlint/binding-win32-arm64-msvc@1.80.0':
- optional: true
-
- '@oxlint/binding-win32-ia32-msvc@1.80.0':
- optional: true
-
- '@oxlint/binding-win32-x64-msvc@1.80.0':
- optional: true
-
- '@paralleldrive/cuid2@2.3.1':
- dependencies:
- '@noble/hashes': 1.8.0
-
- '@pinojs/redact@0.4.0': {}
-
- '@pkgjs/parseargs@0.11.0':
- optional: true
-
- '@polka/url@1.0.0-next.29': {}
-
- '@quansync/fs@1.0.0':
- dependencies:
- quansync: 1.0.0
-
- '@radix-ui/number@1.1.1': {}
-
- '@radix-ui/primitive@1.1.3': {}
-
- '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/primitive': 1.1.3
- '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-direction': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
-
- '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
-
- '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/primitive': 1.1.3
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
-
- '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-slot': 1.2.3(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
-
- '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
-
- '@radix-ui/react-context@1.1.2(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
-
- '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/primitive': 1.1.3
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-slot': 1.2.3(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6)
- aria-hidden: 1.2.6
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- react-remove-scroll: 2.7.2(@types/react@19.2.15)(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ optional: true
- '@radix-ui/react-direction@1.1.1(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@oxlint-tsgolint/win32-arm64@7.0.2001':
+ optional: true
- '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/primitive': 1.1.3
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@oxlint-tsgolint/win32-x64@7.0.2001':
+ optional: true
- '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@oxlint/binding-android-arm-eabi@1.80.0':
+ optional: true
- '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@oxlint/binding-android-arm64@1.80.0':
+ optional: true
- '@radix-ui/react-id@1.1.1(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@oxlint/binding-darwin-arm64@1.80.0':
+ optional: true
- '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/primitive': 1.1.3
- '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-direction': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@oxlint/binding-darwin-x64@1.80.0':
+ optional: true
- '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/primitive': 1.1.3
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-slot': 1.2.3(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6)
- aria-hidden: 1.2.6
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- react-remove-scroll: 2.7.2(@types/react@19.2.15)(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@oxlint/binding-freebsd-x64@1.80.0':
+ optional: true
- '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@floating-ui/react-dom': 2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/rect': 1.1.1
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@oxlint/binding-linux-arm-gnueabihf@1.80.0':
+ optional: true
- '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@oxlint/binding-linux-arm-musleabihf@1.80.0':
+ optional: true
- '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@oxlint/binding-linux-arm64-gnu@1.80.0':
+ optional: true
- '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/react-slot': 1.2.3(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@oxlint/binding-linux-arm64-musl@1.80.0':
+ optional: true
- '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/primitive': 1.1.3
- '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-direction': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@oxlint/binding-linux-ppc64-gnu@1.80.0':
+ optional: true
- '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/number': 1.1.1
- '@radix-ui/primitive': 1.1.3
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-direction': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@oxlint/binding-linux-riscv64-gnu@1.80.0':
+ optional: true
- '@radix-ui/react-slot@1.2.3(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@oxlint/binding-linux-riscv64-musl@1.80.0':
+ optional: true
- '@radix-ui/react-slot@1.2.4(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@oxlint/binding-linux-s390x-gnu@1.80.0':
+ optional: true
- '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/primitive': 1.1.3
- '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-direction': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@oxlint/binding-linux-x64-gnu@1.80.0':
+ optional: true
- '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@oxlint/binding-linux-x64-musl@1.80.0':
+ optional: true
- '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@oxlint/binding-openharmony-arm64@1.80.0':
+ optional: true
- '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@oxlint/binding-win32-arm64-msvc@1.80.0':
+ optional: true
- '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@oxlint/binding-win32-ia32-msvc@1.80.0':
+ optional: true
- '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@oxlint/binding-win32-x64-msvc@1.80.0':
+ optional: true
- '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.15)(react@19.2.6)':
+ '@paralleldrive/cuid2@2.3.1':
dependencies:
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@noble/hashes': 1.8.0
- '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- '@radix-ui/rect': 1.1.1
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@pinojs/redact@0.4.0': {}
- '@radix-ui/react-use-size@1.1.1(@types/react@19.2.15)(react@19.2.6)':
- dependencies:
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- react: 19.2.6
- optionalDependencies:
- '@types/react': 19.2.15
+ '@pkgjs/parseargs@0.11.0':
+ optional: true
- '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
- dependencies:
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- optionalDependencies:
- '@types/react': 19.2.15
- '@types/react-dom': 19.2.3(@types/react@19.2.15)
+ '@polka/url@1.0.0-next.29': {}
- '@radix-ui/rect@1.1.1': {}
+ '@quansync/fs@1.0.0':
+ dependencies:
+ quansync: 1.0.0
'@remixicon/react@4.9.0(react@19.2.6)':
dependencies:
@@ -8604,52 +8316,52 @@ snapshots:
'@sec-ant/readable-stream@0.4.1': {}
- '@shikijs/core@4.1.0':
+ '@shikijs/core@4.4.3':
dependencies:
- '@shikijs/primitive': 4.1.0
- '@shikijs/types': 4.1.0
+ '@shikijs/primitive': 4.4.3
+ '@shikijs/types': 4.4.3
'@shikijs/vscode-textmate': 10.0.2
- '@types/hast': 3.0.4
+ '@types/hast': 3.0.5
hast-util-to-html: 9.0.5
- '@shikijs/engine-javascript@4.1.0':
+ '@shikijs/engine-javascript@4.4.3':
dependencies:
- '@shikijs/types': 4.1.0
+ '@shikijs/types': 4.4.3
'@shikijs/vscode-textmate': 10.0.2
oniguruma-to-es: 4.3.6
- '@shikijs/engine-oniguruma@4.1.0':
+ '@shikijs/engine-oniguruma@4.4.3':
dependencies:
- '@shikijs/types': 4.1.0
+ '@shikijs/types': 4.4.3
'@shikijs/vscode-textmate': 10.0.2
- '@shikijs/langs@4.1.0':
+ '@shikijs/langs@4.4.3':
dependencies:
- '@shikijs/types': 4.1.0
+ '@shikijs/types': 4.4.3
- '@shikijs/primitive@4.1.0':
+ '@shikijs/primitive@4.4.3':
dependencies:
- '@shikijs/types': 4.1.0
+ '@shikijs/types': 4.4.3
'@shikijs/vscode-textmate': 10.0.2
- '@types/hast': 3.0.4
+ '@types/hast': 3.0.5
- '@shikijs/themes@4.1.0':
+ '@shikijs/themes@4.4.3':
dependencies:
- '@shikijs/types': 4.1.0
+ '@shikijs/types': 4.4.3
- '@shikijs/twoslash@4.1.0(typescript@6.0.3)':
+ '@shikijs/twoslash@4.4.3(typescript@6.0.3)':
dependencies:
- '@shikijs/core': 4.1.0
- '@shikijs/types': 4.1.0
- twoslash: 0.3.8(typescript@6.0.3)
+ '@shikijs/core': 4.4.3
+ '@shikijs/types': 4.4.3
+ twoslash: 0.3.9(typescript@6.0.3)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
- '@shikijs/types@4.1.0':
+ '@shikijs/types@4.4.3':
dependencies:
'@shikijs/vscode-textmate': 10.0.2
- '@types/hast': 3.0.4
+ '@types/hast': 3.0.5
'@shikijs/vscode-textmate@10.0.2': {}
@@ -8708,15 +8420,15 @@ snapshots:
dependencies:
acorn: 8.16.0
- '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))':
+ '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))':
dependencies:
- '@sveltejs/kit': 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@sveltejs/kit': 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
- '@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@standard-schema/spec': 1.1.0
'@sveltejs/acorn-typescript': 1.0.10(acorn@8.16.0)
- '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@types/cookie': 0.6.0
acorn: 8.16.0
cookie: 0.6.0
@@ -8728,16 +8440,16 @@ snapshots:
set-cookie-parser: 3.1.0
sirv: 3.0.2
svelte: 5.56.0
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
optionalDependencies:
typescript: 6.0.3
optional: true
- '@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@standard-schema/spec': 1.1.0
'@sveltejs/acorn-typescript': 1.0.10(acorn@8.16.0)
- '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@types/cookie': 0.6.0
acorn: 8.16.0
cookie: 0.6.0
@@ -8749,7 +8461,7 @@ snapshots:
set-cookie-parser: 3.1.0
sirv: 3.0.2
svelte: 5.56.0
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
optionalDependencies:
typescript: 6.0.3
@@ -8764,24 +8476,24 @@ snapshots:
transitivePeerDependencies:
- typescript
- '@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
deepmerge: 4.3.1
magic-string: 0.30.21
obug: 2.1.1
svelte: 5.56.0
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitefu: 1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vitefu: 1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
optional: true
- '@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
deepmerge: 4.3.1
magic-string: 0.30.21
obug: 2.1.1
svelte: 5.56.0
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitefu: 1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vitefu: 1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@swc/helpers@0.5.15':
dependencies:
@@ -8861,19 +8573,19 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 4.3.0
- '@tailwindcss/vite@4.3.0(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tailwindcss/vite@4.3.0(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tailwindcss/node': 4.3.0
'@tailwindcss/oxide': 4.3.0
tailwindcss: 4.3.0
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- '@tailwindcss/vite@4.3.0(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tailwindcss/vite@4.3.0(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tailwindcss/node': 4.3.0
'@tailwindcss/oxide': 4.3.0
tailwindcss: 4.3.0
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
'@tanstack/devtools-bundler-core@0.1.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
dependencies:
@@ -8912,13 +8624,13 @@ snapshots:
transitivePeerDependencies:
- csstype
- '@tanstack/devtools-vite@0.8.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/devtools-vite@0.8.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/devtools-bundler-core': 0.1.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
'@tanstack/devtools-client': 0.0.8
'@tanstack/devtools-event-bus': 0.4.3
chalk: 5.6.2
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -9058,7 +8770,7 @@ snapshots:
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- '@tanstack/react-start-rsc@0.1.17(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start-rsc@0.1.17(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-server': 1.167.13(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -9066,7 +8778,7 @@ snapshots:
'@tanstack/router-utils': 1.162.1
'@tanstack/start-client-core': 1.170.6
'@tanstack/start-fn-stubs': 1.162.0
- '@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-server-core': 1.169.8(crossws@0.4.5(srvx@0.11.16))
'@tanstack/start-storage-context': 1.167.10
pathe: 2.0.3
@@ -9080,14 +8792,14 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/react-start-rsc@0.1.23(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start-rsc@0.1.23(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/router-core': 1.171.13
'@tanstack/router-utils': 1.162.2
'@tanstack/start-client-core': 1.170.11
'@tanstack/start-fn-stubs': 1.162.0
- '@tanstack/start-plugin-core': 1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-server-core': 1.169.13(crossws@0.4.5(srvx@0.11.16))
'@tanstack/start-storage-context': 1.167.15
pathe: 2.0.3
@@ -9101,14 +8813,14 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/react-start-rsc@0.1.48(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start-rsc@0.1.48(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/router-core': 1.171.27
'@tanstack/router-utils': 1.162.2
'@tanstack/start-client-core': 1.170.27
'@tanstack/start-fn-stubs': 1.162.0
- '@tanstack/start-plugin-core': 1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-storage-context': 1.167.29
pathe: 2.0.3
react: 19.2.6
@@ -9153,21 +8865,21 @@ snapshots:
transitivePeerDependencies:
- crossws
- '@tanstack/react-start@1.168.18(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start@1.168.18(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-client': 1.168.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/react-start-rsc': 0.1.17(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/react-start-rsc': 0.1.17(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/react-start-server': 1.167.13(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/router-utils': 1.162.1
'@tanstack/start-client-core': 1.170.6
- '@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-server-core': 1.169.8(crossws@0.4.5(srvx@0.11.16))
pathe: 2.0.3
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
optionalDependencies:
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@rspack/core'
- crossws
@@ -9176,21 +8888,21 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/react-start@1.168.24(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start@1.168.24(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-client': 1.168.12(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/react-start-rsc': 0.1.23(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/react-start-rsc': 0.1.23(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/react-start-server': 1.167.18(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/router-utils': 1.162.2
'@tanstack/start-client-core': 1.170.11
- '@tanstack/start-plugin-core': 1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-server-core': 1.169.13(crossws@0.4.5(srvx@0.11.16))
pathe: 2.0.3
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
optionalDependencies:
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@rspack/core'
- crossws
@@ -9199,21 +8911,21 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/react-start@1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/react-start@1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-client': 1.168.30(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/react-start-rsc': 0.1.48(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/react-start-rsc': 0.1.48(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/react-start-server': 1.167.37(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/router-utils': 1.162.2
'@tanstack/start-client-core': 1.170.27
- '@tanstack/start-plugin-core': 1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/start-plugin-core': 1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/start-server-core': 1.169.31(crossws@0.4.5(srvx@0.11.16))
pathe: 2.0.3
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
optionalDependencies:
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@rspack/core'
- crossws
@@ -9313,7 +9025,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@babel/core': 7.29.7
'@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
@@ -9330,12 +9042,12 @@ snapshots:
zod: 4.4.3
optionalDependencies:
'@tanstack/react-router': 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
transitivePeerDependencies:
- supports-color
- '@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@babel/core': 7.29.7
'@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
@@ -9352,12 +9064,12 @@ snapshots:
zod: 4.4.3
optionalDependencies:
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
transitivePeerDependencies:
- supports-color
- '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@babel/core': 7.29.7
'@babel/template': 7.29.7
@@ -9370,12 +9082,12 @@ snapshots:
zod: 4.4.3
optionalDependencies:
'@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
transitivePeerDependencies:
- supports-color
- '@tanstack/router-plugin@1.168.35(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/router-plugin@1.168.35(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@babel/core': 7.29.7
'@babel/template': 7.29.7
@@ -9388,8 +9100,8 @@ snapshots:
zod: 4.4.3
optionalDependencies:
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite-plugin-solid: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
transitivePeerDependencies:
- supports-color
@@ -9448,7 +9160,7 @@ snapshots:
'@tanstack/start-fn-stubs@1.162.0': {}
- '@tanstack/start-plugin-core@1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/start-plugin-core@1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/core': 7.29.7
@@ -9456,24 +9168,24 @@ snapshots:
'@rolldown/pluginutils': 1.0.1
'@tanstack/router-core': 1.171.8
'@tanstack/router-generator': 1.167.12
- '@tanstack/router-plugin': 1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/router-plugin': 1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/router-utils': 1.162.1
'@tanstack/start-client-core': 1.170.6
'@tanstack/start-server-core': 1.169.8(crossws@0.4.5(srvx@0.11.16))
exsolve: 1.0.8
lightningcss: 1.32.0
pathe: 2.0.3
- picomatch: 4.0.4
+ picomatch: 4.0.7
seroval: 1.5.4
source-map: 0.7.6
srvx: 0.11.16
tinyglobby: 0.2.17
ufo: 1.6.4
- vitefu: 1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitefu: 1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
xmlbuilder2: 4.0.3
zod: 4.4.3
optionalDependencies:
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@tanstack/react-router'
- crossws
@@ -9481,14 +9193,14 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/start-plugin-core@1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/start-plugin-core@1.171.16(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/core': 7.29.7
'@babel/types': 7.29.7
'@tanstack/router-core': 1.171.13
'@tanstack/router-generator': 1.167.17
- '@tanstack/router-plugin': 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/router-plugin': 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/router-utils': 1.162.2
'@tanstack/start-server-core': 1.169.13(crossws@0.4.5(srvx@0.11.16))
exsolve: 1.0.8
@@ -9500,11 +9212,11 @@ snapshots:
srvx: 0.11.16
tinyglobby: 0.2.17
ufo: 1.6.4
- vitefu: 1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitefu: 1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
xmlbuilder2: 4.0.3
zod: 4.4.3
optionalDependencies:
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@tanstack/react-router'
- crossws
@@ -9512,30 +9224,30 @@ snapshots:
- vite-plugin-solid
- webpack
- '@tanstack/start-plugin-core@1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@tanstack/start-plugin-core@1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.5(srvx@0.11.16))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/core': 7.29.7
'@babel/types': 7.29.7
'@tanstack/router-core': 1.171.27
'@tanstack/router-generator': 1.167.33
- '@tanstack/router-plugin': 1.168.35(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@tanstack/router-plugin': 1.168.35(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tanstack/router-utils': 1.162.2
'@tanstack/start-server-core': 1.169.31(crossws@0.4.5(srvx@0.11.16))
exsolve: 1.0.8
lightningcss: 1.32.0
pathe: 2.0.3
- picomatch: 4.0.4
+ picomatch: 4.0.7
seroval: 1.6.4
source-map: 0.7.6
srvx: 0.11.16
tinyglobby: 0.2.17
ufo: 1.6.4
- vitefu: 1.1.3(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitefu: 1.1.3(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
xmlbuilder2: 4.0.3
zod: 4.4.3
optionalDependencies:
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@tanstack/react-router'
- crossws
@@ -9629,14 +9341,14 @@ snapshots:
dependencies:
svelte: 5.56.0
- '@testing-library/svelte@5.3.1(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.8)':
+ '@testing-library/svelte@5.3.1(svelte@5.56.0)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.8)':
dependencies:
'@testing-library/dom': 10.4.1
'@testing-library/svelte-core': 1.0.0(svelte@5.56.0)
svelte: 5.56.0
optionalDependencies:
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@tokenizer/inflate@0.4.1':
dependencies:
@@ -9872,6 +9584,10 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
+ '@types/hast@3.0.5':
+ dependencies:
+ '@types/unist': 3.0.3
+
'@types/http-errors@2.0.5': {}
'@types/jsesc@2.5.1': {}
@@ -9963,33 +9679,33 @@ snapshots:
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
- '@vercel/analytics@2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@6.0.3))':
+ '@vercel/analytics@2.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(svelte@5.56.0)(vue@3.5.35(typescript@6.0.3))':
optionalDependencies:
- '@sveltejs/kit': 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@sveltejs/kit': 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.0)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.0)(typescript@6.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
next: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
svelte: 5.56.0
vue: 3.5.35(typescript@6.0.3)
- '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@rolldown/pluginutils': 1.0.1
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@rolldown/pluginutils': 1.0.1
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@rolldown/pluginutils': 1.0.1
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- '@vitejs/plugin-vue@6.0.7(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.35(typescript@6.0.3))':
+ '@vitejs/plugin-vue@6.0.7(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.35(typescript@6.0.3))':
dependencies:
'@rolldown/pluginutils': 1.0.1
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
vue: 3.5.35(typescript@6.0.3)
'@vitest/coverage-v8@4.1.8(vitest@4.1.8)':
@@ -10004,7 +9720,7 @@ snapshots:
obug: 2.1.1
std-env: 4.1.0
tinyrainbow: 3.1.0
- vitest: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitest: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vitest/expect@4.1.8':
dependencies:
@@ -10015,21 +9731,21 @@ snapshots:
chai: 6.2.2
tinyrainbow: 3.1.0
- '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@vitest/spy': 4.1.8
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
+ '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))':
dependencies:
'@vitest/spy': 4.1.8
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
'@vitest/pretty-format@4.1.8':
dependencies:
@@ -10105,7 +9821,7 @@ snapshots:
alien-signals: 3.2.1
muggle-string: 0.4.1
path-browserify: 1.0.1
- picomatch: 4.0.4
+ picomatch: 4.0.7
'@vue/reactivity@3.5.35':
dependencies:
@@ -10145,6 +9861,44 @@ snapshots:
typescript: 6.0.3
vue: 3.5.35(typescript@6.0.3)
+ '@yuku-analyzer/binding-android-arm64@0.9.3':
+ optional: true
+
+ '@yuku-analyzer/binding-darwin-arm64@0.9.3':
+ optional: true
+
+ '@yuku-analyzer/binding-darwin-x64@0.9.3':
+ optional: true
+
+ '@yuku-analyzer/binding-freebsd-x64@0.9.3':
+ optional: true
+
+ '@yuku-analyzer/binding-linux-arm-gnu@0.9.3':
+ optional: true
+
+ '@yuku-analyzer/binding-linux-arm-musl@0.9.3':
+ optional: true
+
+ '@yuku-analyzer/binding-linux-arm64-gnu@0.9.3':
+ optional: true
+
+ '@yuku-analyzer/binding-linux-arm64-musl@0.9.3':
+ optional: true
+
+ '@yuku-analyzer/binding-linux-x64-gnu@0.9.3':
+ optional: true
+
+ '@yuku-analyzer/binding-linux-x64-musl@0.9.3':
+ optional: true
+
+ '@yuku-analyzer/binding-win32-arm64@0.9.3':
+ optional: true
+
+ '@yuku-analyzer/binding-win32-x64@0.9.3':
+ optional: true
+
+ '@yuku-toolchain/types@0.9.3': {}
+
abbrev@2.0.0: {}
abstract-logging@2.0.1: {}
@@ -10195,10 +9949,6 @@ snapshots:
argparse@2.0.1: {}
- aria-hidden@1.2.6:
- dependencies:
- tslib: 2.8.1
-
aria-query@5.3.0:
dependencies:
dequal: 2.0.3
@@ -10372,6 +10122,10 @@ snapshots:
clsx@2.1.1: {}
+ cnfast@0.0.8: {}
+
+ cnfast@0.1.0: {}
+
collapse-white-space@2.1.0: {}
color-convert@2.0.1:
@@ -10897,6 +10651,35 @@ snapshots:
'@esbuild/win32-ia32': 0.28.0
'@esbuild/win32-x64': 0.28.0
+ esbuild@0.28.2:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.28.2
+ '@esbuild/android-arm': 0.28.2
+ '@esbuild/android-arm64': 0.28.2
+ '@esbuild/android-x64': 0.28.2
+ '@esbuild/darwin-arm64': 0.28.2
+ '@esbuild/darwin-x64': 0.28.2
+ '@esbuild/freebsd-arm64': 0.28.2
+ '@esbuild/freebsd-x64': 0.28.2
+ '@esbuild/linux-arm': 0.28.2
+ '@esbuild/linux-arm64': 0.28.2
+ '@esbuild/linux-ia32': 0.28.2
+ '@esbuild/linux-loong64': 0.28.2
+ '@esbuild/linux-mips64el': 0.28.2
+ '@esbuild/linux-ppc64': 0.28.2
+ '@esbuild/linux-riscv64': 0.28.2
+ '@esbuild/linux-s390x': 0.28.2
+ '@esbuild/linux-x64': 0.28.2
+ '@esbuild/netbsd-arm64': 0.28.2
+ '@esbuild/netbsd-x64': 0.28.2
+ '@esbuild/openbsd-arm64': 0.28.2
+ '@esbuild/openbsd-x64': 0.28.2
+ '@esbuild/openharmony-arm64': 0.28.2
+ '@esbuild/sunos-x64': 0.28.2
+ '@esbuild/win32-arm64': 0.28.2
+ '@esbuild/win32-ia32': 0.28.2
+ '@esbuild/win32-x64': 0.28.2
+
escalade@3.2.0: {}
escape-html@1.0.3: {}
@@ -11123,6 +10906,8 @@ snapshots:
pkg-types: 1.3.1
yaml: 2.9.0
+ flexsearch@0.8.212: {}
+
foreground-child@3.3.1:
dependencies:
cross-spawn: 7.0.6
@@ -11144,10 +10929,10 @@ snapshots:
forwarded@0.2.0: {}
- framer-motion@12.40.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
+ framer-motion@13.1.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
dependencies:
- motion-dom: 12.40.0
- motion-utils: 12.39.0
+ motion-dom: 13.1.1
+ motion-utils: 13.0.0
tslib: 2.8.1
optionalDependencies:
react: 19.2.6
@@ -11158,33 +10943,36 @@ snapshots:
fsevents@2.3.3:
optional: true
- fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3):
+ fumadocs-core@16.15.4(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.15)(flexsearch@0.8.212)(lucide-react@1.34.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3):
dependencies:
- '@orama/orama': 3.1.18
+ '@fumari/image-size': 0.1.0
estree-util-value-to-estree: 3.5.0
github-slugger: 2.0.0
hast-util-to-estree: 3.1.3
hast-util-to-jsx-runtime: 2.3.6
- js-yaml: 4.1.1
mdast-util-mdx: 3.0.0
mdast-util-to-markdown: 2.1.2
+ npm-to-yarn: 3.2.0
remark: 15.0.1
remark-gfm: 4.0.1
remark-rehype: 11.1.2
scroll-into-view-if-needed: 3.1.0
- shiki: 4.1.0
+ shiki: 4.4.3
tinyglobby: 0.2.17
unified: 11.0.5
unist-util-visit: 5.1.0
vfile: 6.0.3
+ yaml: 2.9.0
+ zbsearch: 4.0.0
optionalDependencies:
'@mdx-js/mdx': 3.1.1
'@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@types/estree-jsx': 1.0.5
- '@types/hast': 3.0.4
+ '@types/hast': 3.0.5
'@types/mdast': 4.0.4
'@types/react': 19.2.15
- lucide-react: 1.17.0(react@19.2.6)
+ flexsearch: 0.8.212
+ lucide-react: 1.34.0(react@19.2.6)
next: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
@@ -11192,24 +10980,27 @@ snapshots:
transitivePeerDependencies:
- supports-color
- fumadocs-mdx@15.0.10(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ fumadocs-mdx@15.4.0(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.15.4(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.15)(flexsearch@0.8.212)(lucide-react@1.34.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)(rolldown@1.0.3)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
'@mdx-js/mdx': 3.1.1
'@standard-schema/spec': 1.1.0
chokidar: 5.0.0
- esbuild: 0.28.0
+ esbuild: 0.28.2
estree-util-value-to-estree: 3.5.0
- fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
- js-yaml: 4.1.1
+ fumadocs-core: 16.15.4(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.15)(flexsearch@0.8.212)(lucide-react@1.34.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
+ github-slugger: 2.0.0
+ magic-string: 1.2.3
mdast-util-mdx: 3.0.0
picocolors: 1.1.1
- picomatch: 4.0.4
- tinyexec: 1.2.3
+ picomatch: 4.0.7
+ tinyexec: 1.3.0
tinyglobby: 0.2.17
unified: 11.0.5
unist-util-remove-position: 5.0.0
unist-util-visit: 5.1.0
vfile: 6.0.3
+ yaml: 2.9.0
+ yuku-analyzer: 0.9.3
zod: 4.4.3
optionalDependencies:
'@types/mdast': 4.0.4
@@ -11218,67 +11009,32 @@ snapshots:
next: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
rolldown: 1.0.3
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- supports-color
- fumadocs-twoslash@3.2.0(1d030e86b7f7b9132e23a7c3ab6b57d0):
+ fumadocs-twoslash@3.3.0(4805773098752cdf60a478d7cf7e5e6e):
dependencies:
- '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@shikijs/twoslash': 4.1.0(typescript@6.0.3)
- fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
- fumadocs-ui: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)
+ '@base-ui/react': 1.7.0(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@shikijs/twoslash': 4.4.3(typescript@6.0.3)
+ cnfast: 0.0.8
+ fumadocs-core: 16.15.4(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.15)(flexsearch@0.8.212)(lucide-react@1.34.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
+ fumadocs-ui: '@fumadocs/base-ui@16.15.4(@types/mdx@2.0.13)(@types/react@19.2.15)(fumadocs-core@16.15.4(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.15)(flexsearch@0.8.212)(lucide-react@1.34.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0)'
mdast-util-from-markdown: 2.0.3
mdast-util-gfm: 3.1.0
mdast-util-to-hast: 13.2.1
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- shiki: 4.1.0
- tailwind-merge: 3.6.0
- twoslash: 0.3.8(typescript@6.0.3)
+ shiki: 4.4.3
+ twoslash: 0.3.9(typescript@6.0.3)
optionalDependencies:
'@types/react': 19.2.15
transitivePeerDependencies:
- - '@types/react-dom'
+ - '@date-fns/tz'
+ - date-fns
- supports-color
- typescript
- fumadocs-ui@16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.0):
- dependencies:
- '@fumadocs/tailwind': 0.0.5(@tailwindcss/oxide@4.3.0)(tailwindcss@4.3.0)
- '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-direction': 1.1.1(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@radix-ui/react-slot': 1.2.4(@types/react@19.2.15)(react@19.2.6)
- '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- class-variance-authority: 0.7.1
- fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.15)(lucide-react@1.17.0(react@19.2.6))(next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)
- lucide-react: 1.17.0(react@19.2.6)
- motion: 12.40.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- next-themes: 0.4.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- react-remove-scroll: 2.7.2(@types/react@19.2.15)(react@19.2.6)
- rehype-raw: 7.0.0
- scroll-into-view-if-needed: 3.1.0
- shiki: 4.1.0
- tailwind-merge: 3.6.0
- unist-util-visit: 5.1.0
- optionalDependencies:
- '@types/mdx': 2.0.13
- '@types/react': 19.2.15
- next: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- transitivePeerDependencies:
- - '@emotion/is-prop-valid'
- - '@tailwindcss/oxide'
- - '@types/react-dom'
- - tailwindcss
-
function-bind@1.1.2: {}
fzf@0.5.2: {}
@@ -11820,7 +11576,7 @@ snapshots:
dependencies:
react: 19.2.6
- lucide-react@1.17.0(react@19.2.6):
+ lucide-react@1.34.0(react@19.2.6):
dependencies:
react: 19.2.6
@@ -11830,6 +11586,10 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
+ magic-string@1.2.3:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
magicast@0.5.3:
dependencies:
'@babel/parser': 7.29.7
@@ -12363,15 +12123,15 @@ snapshots:
pkg-types: 1.3.1
ufo: 1.6.4
- motion-dom@12.40.0:
+ motion-dom@13.1.1:
dependencies:
- motion-utils: 12.39.0
+ motion-utils: 13.0.0
- motion-utils@12.39.0: {}
+ motion-utils@13.0.0: {}
- motion@12.40.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
+ motion@13.1.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
dependencies:
- framer-motion: 12.40.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ framer-motion: 13.1.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
tslib: 2.8.1
optionalDependencies:
react: 19.2.6
@@ -12444,7 +12204,7 @@ snapshots:
nf3@0.3.17: {}
- nitro@3.0.260522-beta(chokidar@5.0.0)(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3))(jiti@2.7.0)(lru-cache@11.5.1)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ nitro@3.0.260522-beta(chokidar@5.0.0)(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3))(jiti@2.7.0)(lru-cache@11.5.1)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
consola: 3.4.2
crossws: 0.4.5(srvx@0.11.16)
@@ -12462,7 +12222,7 @@ snapshots:
unstorage: 2.0.0-alpha.7(chokidar@5.0.0)(db0@0.3.4(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3)))(lru-cache@11.5.1)(ofetch@2.0.0-alpha.3)
optionalDependencies:
jiti: 2.7.0
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -12520,6 +12280,8 @@ snapshots:
path-key: 4.0.0
unicorn-magic: 0.3.0
+ npm-to-yarn@3.2.0: {}
+
nypm@0.6.9:
dependencies:
citty: 0.2.2
@@ -13013,6 +12775,8 @@ snapshots:
require-from-string@2.0.2: {}
+ reselect@5.3.0: {}
+
resolve-from@4.0.0: {}
resolve-from@5.0.0: {}
@@ -13209,16 +12973,16 @@ snapshots:
shell-quote@1.8.4: {}
- shiki@4.1.0:
+ shiki@4.4.3:
dependencies:
- '@shikijs/core': 4.1.0
- '@shikijs/engine-javascript': 4.1.0
- '@shikijs/engine-oniguruma': 4.1.0
- '@shikijs/langs': 4.1.0
- '@shikijs/themes': 4.1.0
- '@shikijs/types': 4.1.0
+ '@shikijs/core': 4.4.3
+ '@shikijs/engine-javascript': 4.4.3
+ '@shikijs/engine-oniguruma': 4.4.3
+ '@shikijs/langs': 4.4.3
+ '@shikijs/themes': 4.4.3
+ '@shikijs/types': 4.4.3
'@shikijs/vscode-textmate': 10.0.2
- '@types/hast': 3.0.4
+ '@types/hast': 3.0.5
side-channel-list@1.0.1:
dependencies:
@@ -13555,12 +13319,12 @@ snapshots:
'@turbo/windows-64': 2.9.16
'@turbo/windows-arm64': 2.9.16
- twoslash-protocol@0.3.8: {}
+ twoslash-protocol@0.3.9: {}
- twoslash@0.3.8(typescript@6.0.3):
+ twoslash@0.3.9(typescript@6.0.3):
dependencies:
'@typescript/vfs': 1.6.4(typescript@6.0.3)
- twoslash-protocol: 0.3.8
+ twoslash-protocol: 0.3.9
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
@@ -13677,7 +13441,7 @@ snapshots:
unplugin@3.0.0:
dependencies:
'@jridgewell/remapping': 2.3.5
- picomatch: 4.0.4
+ picomatch: 4.0.7
webpack-virtual-modules: 0.6.2
unstorage@2.0.0-alpha.7(chokidar@5.0.0)(db0@0.3.4(drizzle-orm@1.0.0-rc.3(@sinclair/typebox@0.34.49)(@types/pg@8.20.0)(zod@4.4.3)))(lru-cache@11.5.1)(ofetch@2.0.0-alpha.3):
@@ -13733,7 +13497,7 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
- vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
'@babel/core': 7.29.7
'@types/babel__core': 7.20.5
@@ -13741,15 +13505,15 @@ snapshots:
merge-anything: 5.1.7
solid-js: 1.9.13
solid-refresh: 0.6.3(solid-js@1.9.13)
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitefu: 1.1.3(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vitefu: 1.1.3(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
optionalDependencies:
'@testing-library/jest-dom': 6.9.1
transitivePeerDependencies:
- supports-color
optional: true
- vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
'@babel/core': 7.29.7
'@types/babel__core': 7.20.5
@@ -13757,15 +13521,15 @@ snapshots:
merge-anything: 5.1.7
solid-js: 1.9.13
solid-refresh: 0.6.3(solid-js@1.9.13)
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitefu: 1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vitefu: 1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
optionalDependencies:
'@testing-library/jest-dom': 6.9.1
transitivePeerDependencies:
- supports-color
optional: true
- vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
'@babel/core': 7.29.7
'@types/babel__core': 7.20.5
@@ -13773,24 +13537,24 @@ snapshots:
merge-anything: 5.1.7
solid-js: 1.9.13
solid-refresh: 0.6.3(solid-js@1.9.13)
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitefu: 1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vitefu: 1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
optionalDependencies:
'@testing-library/jest-dom': 6.9.1
transitivePeerDependencies:
- supports-color
- vite-tsconfig-paths@6.1.1(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vite-tsconfig-paths@6.1.1(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
debug: 4.4.3
globrex: 0.1.2
tsconfck: 3.1.6(typescript@6.0.3)
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
transitivePeerDependencies:
- supports-color
- typescript
- vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0):
+ vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
@@ -13799,13 +13563,13 @@ snapshots:
tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 22.19.20
- esbuild: 0.28.0
+ esbuild: 0.28.2
fsevents: 2.3.3
jiti: 2.7.0
tsx: 4.22.4
yaml: 2.9.0
- vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0):
+ vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
@@ -13814,13 +13578,13 @@ snapshots:
tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 24.10.0
- esbuild: 0.28.0
+ esbuild: 0.28.2
fsevents: 2.3.3
jiti: 2.7.0
tsx: 4.22.4
yaml: 2.9.0
- vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0):
+ vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
@@ -13829,28 +13593,28 @@ snapshots:
tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 25.9.1
- esbuild: 0.28.0
+ esbuild: 0.28.2
fsevents: 2.3.3
jiti: 2.7.0
tsx: 4.22.4
yaml: 2.9.0
- vitefu@1.1.3(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vitefu@1.1.3(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
optionalDependencies:
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitefu@1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vitefu@1.1.3(vite@8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
optionalDependencies:
- vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@24.10.0)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitefu@1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vitefu@1.1.3(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
optionalDependencies:
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
- vitest@4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vitest@4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
'@vitest/expect': 4.1.8
- '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vitest/pretty-format': 4.1.8
'@vitest/runner': 4.1.8
'@vitest/snapshot': 4.1.8
@@ -13867,7 +13631,7 @@ snapshots:
tinyexec: 1.2.3
tinyglobby: 0.2.17
tinyrainbow: 3.1.0
- vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.19.20
@@ -13877,10 +13641,10 @@ snapshots:
transitivePeerDependencies:
- msw
- vitest@4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vitest@4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
'@vitest/expect': 4.1.8
- '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vitest/pretty-format': 4.1.8
'@vitest/runner': 4.1.8
'@vitest/snapshot': 4.1.8
@@ -13897,7 +13661,7 @@ snapshots:
tinyexec: 1.2.3
tinyglobby: 0.2.17
tinyrainbow: 3.1.0
- vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 25.9.1
@@ -14020,6 +13784,30 @@ snapshots:
yoctocolors@2.2.0: {}
+ yuku-analyzer@0.9.3:
+ dependencies:
+ '@yuku-toolchain/types': 0.9.3
+ yuku-ast: 0.9.3
+ optionalDependencies:
+ '@yuku-analyzer/binding-android-arm64': 0.9.3
+ '@yuku-analyzer/binding-darwin-arm64': 0.9.3
+ '@yuku-analyzer/binding-darwin-x64': 0.9.3
+ '@yuku-analyzer/binding-freebsd-x64': 0.9.3
+ '@yuku-analyzer/binding-linux-arm-gnu': 0.9.3
+ '@yuku-analyzer/binding-linux-arm-musl': 0.9.3
+ '@yuku-analyzer/binding-linux-arm64-gnu': 0.9.3
+ '@yuku-analyzer/binding-linux-arm64-musl': 0.9.3
+ '@yuku-analyzer/binding-linux-x64-gnu': 0.9.3
+ '@yuku-analyzer/binding-linux-x64-musl': 0.9.3
+ '@yuku-analyzer/binding-win32-arm64': 0.9.3
+ '@yuku-analyzer/binding-win32-x64': 0.9.3
+
+ yuku-ast@0.9.3:
+ dependencies:
+ '@yuku-toolchain/types': 0.9.3
+
+ zbsearch@4.0.0: {}
+
zimmerframe@1.1.4: {}
zod@4.4.3: {}
From 43a320603bdabfc76822a0a5e6ba7609a128d6e1 Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 14:28:23 +0200
Subject: [PATCH 10/28] ci: allow fumadocs 16.15.4 through pnpm
minimumReleaseAge
pnpm 11 rejects packages newer than one day in frozen CI installs.
Co-authored-by: Cursor
---
pnpm-workspace.yaml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 01269547..8ea5395f 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -7,3 +7,6 @@ allowBuilds:
better-sqlite3: true
esbuild: false
sharp: false
+minimumReleaseAgeExclude:
+ - '@fumadocs/base-ui@16.15.4'
+ - fumadocs-core@16.15.4
From f4d6a8349c73ef8038273d55f50e616a664d0acc Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 14:45:08 +0200
Subject: [PATCH 11/28] feat: add NestJS adapter with guard and Check decorator
Wire Permix into Nest via APP_GUARD so each request gets its own instance, then enforce @Check on handlers without tying the API to Express middleware. Closes #11.
Co-authored-by: Cursor
---
_artifacts/domain_map.yaml | 6 +-
_artifacts/skill_tree.yaml | 4 +-
docs/content/docs/comparison.mdx | 1 +
docs/content/docs/integrations/nest.mdx | 261 +++++++++
docs/content/docs/meta.json | 1 +
examples/nest/main.ts | 67 +++
examples/nest/package.json | 21 +
examples/nest/tsconfig.json | 12 +
oxlint.config.ts | 7 +
permix/package.json | 19 +
permix/skills/README.md | 2 +-
permix/skills/permix/SKILL.md | 5 +-
permix/skills/permix/references/server.md | 31 +-
permix/src/nest/index.ts | 1 +
permix/src/nest/permix.test.ts | 643 ++++++++++++++++++++++
permix/src/nest/permix.ts | 214 +++++++
permix/tsconfig.base.json | 3 +-
permix/tsdown.config.ts | 1 +
pnpm-lock.yaml | 324 ++++++++++-
19 files changed, 1601 insertions(+), 22 deletions(-)
create mode 100644 docs/content/docs/integrations/nest.mdx
create mode 100644 examples/nest/main.ts
create mode 100644 examples/nest/package.json
create mode 100644 examples/nest/tsconfig.json
create mode 100644 permix/src/nest/index.ts
create mode 100644 permix/src/nest/permix.test.ts
create mode 100644 permix/src/nest/permix.ts
diff --git a/_artifacts/domain_map.yaml b/_artifacts/domain_map.yaml
index 318aa7c9..8cfb9fd0 100644
--- a/_artifacts/domain_map.yaml
+++ b/_artifacts/domain_map.yaml
@@ -34,7 +34,7 @@ domains:
slug: server
description: >
Per-request setupMiddleware and checkMiddleware for Express, Hono, Fastify,
- tRPC, oRPC, Node, and Elysia.
+ NestJS, tRPC, oRPC, Node, and Elysia.
- name: 'SSR and hydration'
slug: ssr
@@ -78,7 +78,7 @@ skills:
~all/~any, entity-aware ReBAC rules, isReady/isReadyAsync;
PermixProvider/usePermix/createComponents and SSR dehydrate/hydrate for
React, Vue, Solid, Svelte, Next.js, TanStack Start; setupMiddleware and
- checkMiddleware for Express, Hono, Fastify, tRPC, oRPC, Node, Elysia.
+ checkMiddleware for Express, Hono, Fastify, NestJS, tRPC, oRPC, Node, Elysia.
Single skill with a thin SKILL.md router and three reference files
loaded on demand.
type: core
@@ -94,6 +94,7 @@ skills:
- express
- hono
- fastify
+ - nest
- trpc
- orpc
- node
@@ -140,6 +141,7 @@ skills:
- 'letstri/permix:docs/content/docs/integrations/express.mdx'
- 'letstri/permix:docs/content/docs/integrations/hono.mdx'
- 'letstri/permix:docs/content/docs/integrations/fastify.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/nest.mdx'
- 'letstri/permix:docs/content/docs/integrations/trpc.mdx'
- 'letstri/permix:docs/content/docs/integrations/orpc.mdx'
- 'letstri/permix:docs/content/docs/integrations/node.mdx'
diff --git a/_artifacts/skill_tree.yaml b/_artifacts/skill_tree.yaml
index 70b42866..011bfb17 100644
--- a/_artifacts/skill_tree.yaml
+++ b/_artifacts/skill_tree.yaml
@@ -40,7 +40,7 @@ skills:
isReady/isReadyAsync; PermixProvider/usePermix/createComponents and SSR
dehydrate/hydrate for React, Vue, Solid, Svelte, Next.js, TanStack
Start; setupMiddleware/checkMiddleware for Express, Hono, Fastify,
- tRPC, oRPC, Node, Elysia. Thin router with references loaded on demand.
+ NestJS, tRPC, oRPC, Node, Elysia. Thin router with references loaded on demand.
requires:
- permix-getting-started
subsystems:
@@ -53,6 +53,7 @@ skills:
- express
- hono
- fastify
+ - nest
- trpc
- orpc
- node
@@ -75,6 +76,7 @@ skills:
- 'letstri/permix:docs/content/docs/integrations/express.mdx'
- 'letstri/permix:docs/content/docs/integrations/hono.mdx'
- 'letstri/permix:docs/content/docs/integrations/fastify.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/nest.mdx'
- 'letstri/permix:docs/content/docs/integrations/trpc.mdx'
- 'letstri/permix:docs/content/docs/integrations/orpc.mdx'
- 'letstri/permix:docs/content/docs/integrations/node.mdx'
diff --git a/docs/content/docs/comparison.mdx b/docs/content/docs/comparison.mdx
index d0d9ea67..64899ff7 100644
--- a/docs/content/docs/comparison.mdx
+++ b/docs/content/docs/comparison.mdx
@@ -46,6 +46,7 @@ Built with `pnpm run build` in the `permix` package (`tsdown` for all entries ex
| `permix/hono` | 0.88 kB | adapter |
| `permix/fastify` | 1.04 kB | adapter |
| `permix/elysia` | 0.88 kB | adapter |
+| `permix/nest` | 1.39 kB | adapter |
| `permix/trpc` | 0.96 kB | adapter |
| `permix/orpc` | 0.92 kB | adapter |
| `permix/effect` | 1.28 kB | adapter |
diff --git a/docs/content/docs/integrations/nest.mdx b/docs/content/docs/integrations/nest.mdx
new file mode 100644
index 00000000..4a1ebe3a
--- /dev/null
+++ b/docs/content/docs/integrations/nest.mdx
@@ -0,0 +1,261 @@
+---
+title: NestJS
+description: Learn how to use Permix with NestJS
+---
+
+## Overview
+
+Permix provides a NestJS integration that sets up permissions per request and enforces them with a guard plus a `@Check` decorator. The factory is created using `createPermix` from `permix/nest`.
+
+
+ Before getting started with the NestJS integration, make sure you've completed
+ the initial setup steps in the [Quick Start](/docs/quick-start) guide.
+
+
+
+
+
+
+## Setup
+
+Create a Permix factory and register its guard as a global `APP_GUARD`. The guard always attaches a per-request instance; it only enforces a permission when `@Check` is present.
+
+```ts
+import { Module } from '@nestjs/common'
+import { APP_GUARD } from '@nestjs/core'
+import { createPermix } from 'permix/nest'
+
+interface Post {
+ id: string
+ authorId: string
+ title: string
+ content: string
+}
+
+export const permix = createPermix<{
+ post: [
+ { name: 'create'; type: Post },
+ { name: 'read'; type: Post },
+ { name: 'update'; type: Post },
+ ]
+}>()
+
+@Module({
+ providers: [
+ {
+ provide: APP_GUARD,
+ useValue: permix.guard(({ req }) => {
+ // You can access req.user or other properties to determine permissions
+ return {
+ post: {
+ create: true,
+ read: true,
+ update: false,
+ },
+ }
+ }),
+ },
+ ],
+})
+export class AppModule {}
+```
+
+
+ The guard works with both the Express and Fastify Nest HTTP adapters. It
+ preserves full type safety from your Permix definition.
+
+
+
+
+
+
+## Checking Permissions
+
+Use the `Check` decorator on a handler or controller:
+
+```ts
+import { Controller, Delete, Get, Post, Put } from '@nestjs/common'
+import { permix } from './permix'
+
+@Controller('posts')
+export class PostsController {
+ @Post()
+ @permix.Check('post.create')
+ create() {
+ return { success: true }
+ }
+
+ @Put(':id')
+ @permix.Check((c) => c('post.read') && c('post.update'))
+ update() {
+ return { success: true }
+ }
+
+ @Delete(':id')
+ @permix.Check('post.~all')
+ remove() {
+ return { success: true }
+ }
+
+ @Get()
+ @permix.Check('post.~any')
+ findAll() {
+ return { posts: getAllPosts() }
+ }
+}
+```
+
+
+
+
+
+## Accessing Permix Directly
+
+You can access the Permix instance directly in your route handlers using the `get` function:
+
+```ts
+@Get()
+findAll(@Req() req: Request) {
+ const { check } = permix.getOrThrow(req)
+
+ if (check('post.read')) {
+ return { posts: getAllPosts() }
+ }
+
+ throw new ForbiddenException({
+ error: 'You do not have permission to read posts',
+ })
+}
+```
+
+Entity-based (ReBAC) checks usually run in the handler after the resource is loaded:
+
+```ts
+@Put(':id')
+async update(@Param('id') id: string, @Req() req: Request) {
+ const post = await getPostById(id)
+ const { check } = permix.getOrThrow(req)
+
+ if (!check('post.update', post)) {
+ throw new ForbiddenException({ error: 'You cannot update this post' })
+ }
+
+ return { success: true }
+}
+```
+
+
+
+
+
+## Using Templates
+
+Permix provides a template helper to create reusable permission rule sets:
+
+```ts
+const adminTemplate = permix.template({
+ post: {
+ create: true,
+ read: true,
+ update: true,
+ },
+})
+
+{
+ provide: APP_GUARD,
+ useValue: permix.guard(({ req }) => {
+ if (req.user?.role === 'admin') {
+ return adminTemplate()
+ }
+
+ return {
+ post: {
+ create: false,
+ read: true,
+ update: false,
+ },
+ }
+ }),
+}
+```
+
+
+
+
+
+## Custom Error Handling
+
+By default, a denied `@Check` throws a Nest `ForbiddenException` with `{ error: 'Forbidden' }`. You can customize this by providing an `onForbidden` handler:
+
+### Basic Error Handler
+
+```ts
+const permix = createPermix({
+ onForbidden: () => {
+ throw new ForbiddenException({
+ error: 'Custom forbidden message',
+ })
+ },
+})
+```
+
+### Dynamic Error Handler
+
+You can also throw different responses based on the checked path:
+
+```ts
+const permix = createPermix({
+ onForbidden: ({ path }) => {
+ if (path === 'post.create') {
+ throw new ForbiddenException({
+ error: `You don't have permission for ${path}`,
+ })
+ }
+
+ throw new ForbiddenException({
+ error: 'You do not have permission to perform this action',
+ })
+ },
+})
+```
+
+The `onForbidden` handler receives:
+
+- `req`: The HTTP request object (Express or Fastify)
+- `context`: Nest `ExecutionContext`
+- `path`: The permission path that was checked (or `null` for callback checks)
+- `data`: Optional entity data passed to the check
+
+## Advanced Usage
+
+### Async Permission Rules
+
+You can use async functions in your guard setup:
+
+```ts
+permix.guard(async ({ req }) => {
+ const userPermissions = await getUserPermissions(req.user.id)
+
+ return {
+ post: {
+ create: userPermissions.canCreatePosts,
+ read: userPermissions.canReadPosts,
+ update: userPermissions.canUpdatePosts,
+ },
+ }
+})
+```
+
+### Hooks
+
+You can register hooks at the factory level to listen for events across all requests:
+
+```ts
+permix.hook('check', ({ path, data }) => {
+ console.log(`Permission checked: ${path}`, data)
+})
+```
+
+### Example
+
+You can find the example of the NestJS integration [here](https://github.com/letstri/permix/tree/main/examples/nest).
diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json
index dbd79bfc..36552fe8 100644
--- a/docs/content/docs/meta.json
+++ b/docs/content/docs/meta.json
@@ -29,6 +29,7 @@
"integrations/hono",
"integrations/elysia",
"integrations/fastify",
+ "integrations/nest",
"integrations/effect",
"integrations/drizzle",
"---",
diff --git a/examples/nest/main.ts b/examples/nest/main.ts
new file mode 100644
index 00000000..291b4b7a
--- /dev/null
+++ b/examples/nest/main.ts
@@ -0,0 +1,67 @@
+import 'reflect-metadata'
+import {
+ Controller,
+ ForbiddenException,
+ Get,
+ Module,
+ Req,
+} from '@nestjs/common'
+import { APP_GUARD, NestFactory } from '@nestjs/core'
+import type { ValidateDefinition } from 'permix'
+import { createPermix } from 'permix/nest'
+
+type PermissionsDefinition = ValidateDefinition<{
+ user: ['read', 'write']
+}>
+
+const permix = createPermix({
+ onForbidden: () => {
+ throw new ForbiddenException({
+ error: 'You do not have permission to access this resource',
+ })
+ },
+})
+
+@Controller()
+class AppController {
+ @Get()
+ @permix.Check('user.read')
+ read() {
+ return 'Hello World'
+ }
+
+ @Get('write')
+ @permix.Check('user.write')
+ write() {
+ return 'Hello World'
+ }
+
+ @Get('permix')
+ inspect(@Req() req: { [key: PropertyKey]: unknown }) {
+ return { canRead: permix.getOrThrow(req).check('user.read') }
+ }
+}
+
+@Module({
+ controllers: [AppController],
+ providers: [
+ {
+ provide: APP_GUARD,
+ useValue: permix.guard(() => ({
+ user: {
+ read: true,
+ write: false,
+ },
+ })),
+ },
+ ],
+})
+class AppModule {}
+
+async function bootstrap() {
+ const app = await NestFactory.create(AppModule)
+ await app.listen(3000)
+ console.log('Server is running on port 3000')
+}
+
+bootstrap()
diff --git a/examples/nest/package.json b/examples/nest/package.json
new file mode 100644
index 00000000..425acf92
--- /dev/null
+++ b/examples/nest/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "nest",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "check-types": "tsc --noEmit",
+ "start": "tsx main.ts"
+ },
+ "dependencies": {
+ "@nestjs/common": "^11.1.6",
+ "@nestjs/core": "^11.1.6",
+ "@nestjs/platform-express": "^11.1.6",
+ "permix": "workspace:*",
+ "reflect-metadata": "^0.2.2",
+ "rxjs": "^7.8.2"
+ },
+ "devDependencies": {
+ "tsx": "^4.22.4",
+ "typescript": "^6.0.3"
+ }
+}
diff --git a/examples/nest/tsconfig.json b/examples/nest/tsconfig.json
new file mode 100644
index 00000000..c123cb28
--- /dev/null
+++ b/examples/nest/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true
+ }
+}
diff --git a/oxlint.config.ts b/oxlint.config.ts
index e29a4dc3..5b3df5ee 100644
--- a/oxlint.config.ts
+++ b/oxlint.config.ts
@@ -125,5 +125,12 @@ export default defineConfig({
'react/jsx-key': 'off',
},
},
+ {
+ files: ['permix/src/nest/**', 'examples/nest/**'],
+ rules: {
+ 'class-methods-use-this': 'off',
+ 'typescript/no-extraneous-class': 'off',
+ },
+ },
],
})
diff --git a/permix/package.json b/permix/package.json
index dfbefd33..f2f1958f 100644
--- a/permix/package.json
+++ b/permix/package.json
@@ -10,6 +10,7 @@
"authorization",
"frontend",
"javascript",
+ "nestjs",
"nextjs",
"permissions",
"permissions-management",
@@ -116,6 +117,10 @@
"./tanstack-start": {
"types": "./dist/tanstack-start/index.d.mts",
"import": "./dist/tanstack-start/index.mjs"
+ },
+ "./nest": {
+ "types": "./dist/nest/index.d.mts",
+ "import": "./dist/nest/index.mjs"
}
},
"scripts": {
@@ -129,6 +134,10 @@
"test": "vitest run"
},
"devDependencies": {
+ "@nestjs/common": "^11.2.3",
+ "@nestjs/core": "^11.2.3",
+ "@nestjs/platform-express": "^11.2.3",
+ "@nestjs/testing": "^11.2.3",
"@solidjs/testing-library": "^0.8.10",
"@sveltejs/package": "^2.5.7",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
@@ -148,6 +157,8 @@
"effect": "^3.21.2",
"happy-dom": "^20.9.0",
"react-dom": "^19.2.6",
+ "reflect-metadata": "^0.2.2",
+ "rxjs": "^7.8.2",
"supertest": "^7.2.2",
"svelte": "^5.56.0",
"svelte-check": "^4.5.0",
@@ -159,6 +170,8 @@
"zod": "^4.4.3"
},
"peerDependencies": {
+ "@nestjs/common": ">=10",
+ "@nestjs/core": ">=10",
"@orpc/server": ">=1",
"@tanstack/react-start": ">=1",
"@trpc/server": ">=11",
@@ -177,6 +190,12 @@
"vue": ">=3"
},
"peerDependenciesMeta": {
+ "@nestjs/common": {
+ "optional": true
+ },
+ "@nestjs/core": {
+ "optional": true
+ },
"@orpc/server": {
"optional": true
},
diff --git a/permix/skills/README.md b/permix/skills/README.md
index f2616977..b78dbd84 100644
--- a/permix/skills/README.md
+++ b/permix/skills/README.md
@@ -42,7 +42,7 @@ Restart Cursor or start a new agent chat so skills are picked up.
| Skill | Intent id | When to use |
| --- | --- | --- |
| [permix-getting-started](./permix-getting-started/SKILL.md) | `permix#permix-getting-started` | New project, schema, `setup`, roles/templates |
-| [permix](./permix/SKILL.md) | `permix#permix` | Everything past setup: `check`/ReBAC (`references/check.md`), React/Vue/Solid/Svelte + SSR (`references/frontend.md`), Express/Hono/Fastify/tRPC/oRPC middleware (`references/server.md`) |
+| [permix](./permix/SKILL.md) | `permix#permix` | Everything past setup: `check`/ReBAC (`references/check.md`), React/Vue/Solid/Svelte + SSR (`references/frontend.md`), Express/Hono/Fastify/NestJS/tRPC/oRPC middleware (`references/server.md`) |
## Registry and version history
diff --git a/permix/skills/permix/SKILL.md b/permix/skills/permix/SKILL.md
index b3ebc1b5..45f2b62a 100644
--- a/permix/skills/permix/SKILL.md
+++ b/permix/skills/permix/SKILL.md
@@ -1,7 +1,7 @@
---
name: permix
description: >-
- Applies Permix authorization once a schema exists: permix.check() paths and ReBAC callbacks, frontend bindings (permix/react, permix/vue, permix/solid, permix/svelte) with SSR dehydrate/hydrate for Next.js and TanStack Start, and server middleware (permix/express, hono, fastify, trpc, orpc, node, elysia). Use for anything past initial setup — checking permissions, gating UI, or protecting routes. For creating the schema and first `permix.setup()`, use permix-getting-started first.
+ Applies Permix authorization once a schema exists: permix.check() paths and ReBAC callbacks, frontend bindings (permix/react, permix/vue, permix/solid, permix/svelte) with SSR dehydrate/hydrate for Next.js and TanStack Start, and server middleware (permix/express, hono, fastify, nest, trpc, orpc, node, elysia). Use for anything past initial setup — checking permissions, gating UI, or protecting routes. For creating the schema and first `permix.setup()`, use permix-getting-started first.
metadata:
type: core
library: permix
@@ -22,6 +22,7 @@ sources:
- 'letstri/permix:docs/content/docs/integrations/express.mdx'
- 'letstri/permix:docs/content/docs/integrations/hono.mdx'
- 'letstri/permix:docs/content/docs/integrations/fastify.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/nest.mdx'
- 'letstri/permix:docs/content/docs/integrations/trpc.mdx'
- 'letstri/permix:docs/content/docs/integrations/orpc.mdx'
- 'letstri/permix:docs/content/docs/integrations/node.mdx'
@@ -38,7 +39,7 @@ Assumes a `permix` instance already exists (see **permix-getting-started**). Loa
| --- | --- |
| `permix.check()` paths, callbacks, `~all`/`~any`, ReBAC/ABAC with entity data, `isReady` | [references/check.md](references/check.md) |
| React, Vue, Solid, or Svelte UI — `PermixProvider`, `usePermix`, `createComponents`, SSR `dehydrate`/`hydrate` for Next.js / TanStack Start | [references/frontend.md](references/frontend.md) |
-| Protecting Express, Hono, Fastify, tRPC, oRPC, Node, or Elysia routes — `setupMiddleware`, `checkMiddleware` | [references/server.md](references/server.md) |
+| Protecting Express, Hono, Fastify, NestJS, tRPC, oRPC, Node, or Elysia routes — `setupMiddleware`, `checkMiddleware`, or Nest `guard` / `@Check` | [references/server.md](references/server.md) |
## Rules that apply everywhere
diff --git a/permix/skills/permix/references/server.md b/permix/skills/permix/references/server.md
index 8b8c79d5..0306544f 100644
--- a/permix/skills/permix/references/server.md
+++ b/permix/skills/permix/references/server.md
@@ -4,7 +4,7 @@ Authorization must run on the server. Client checks are UX only.
Docs: https://permix.letstri.dev/docs/integrations/express
-## Pattern (Express-style; similar for Hono, Fastify, Node)
+## Pattern (Express-style; similar for Hono, Fastify, Node, Nest)
Import from the framework subpath, not bare `permix`:
@@ -59,6 +59,32 @@ app.delete(
Denied requests default to `403` with `{ error: 'Forbidden' }`. Customize with `onForbidden` in `createPermix` options.
+### NestJS (`permix/nest`)
+
+Use a global `APP_GUARD` plus `@Check`. The guard always sets up the per-request instance and only enforces a path when the decorator is present:
+
+```ts
+import { APP_GUARD } from '@nestjs/core'
+import { createPermix } from 'permix/nest'
+
+const permix = createPermix<{
+ post: ['create', 'read']
+}>()
+
+{
+ provide: APP_GUARD,
+ useValue: permix.guard(({ req }) => ({
+ post: { create: !!req.user, read: true },
+ })),
+}
+
+@Get()
+@permix.Check('post.read')
+findAll() {}
+```
+
+Entity checks run in the handler after the resource is loaded: `permix.getOrThrow(req).check('post.update', post)`.
+
### Access instance in handlers
```ts
@@ -77,6 +103,7 @@ app.get('/posts/:id', (req, res) => {
| Express | `permix/express` |
| Hono | `permix/hono` |
| Fastify | `permix/fastify` |
+| NestJS | `permix/nest` |
| tRPC | `permix/trpc` |
| oRPC | `permix/orpc` |
| Generic HTTP | `permix/node` or `permix/server` |
@@ -101,7 +128,7 @@ app.use(permix.setupMiddleware(rules))
## Checklist
-- [ ] `setupMiddleware` runs **before** `checkMiddleware` on protected routes
+- [ ] `setupMiddleware` runs **before** `checkMiddleware` on protected routes (Nest: register `permix.guard(...)` as `APP_GUARD` before `@Check`)
- [ ] Rules derived from authenticated `req.user` (or RPC context), not client headers alone
- [ ] Entity checks pass resource data when the action has `type` / `required: true`
- [ ] Same paths as frontend (`post.update`, not ad-hoc strings)
diff --git a/permix/src/nest/index.ts b/permix/src/nest/index.ts
new file mode 100644
index 00000000..60dafabb
--- /dev/null
+++ b/permix/src/nest/index.ts
@@ -0,0 +1 @@
+export * from './permix'
diff --git a/permix/src/nest/permix.test.ts b/permix/src/nest/permix.test.ts
new file mode 100644
index 00000000..27fa3992
--- /dev/null
+++ b/permix/src/nest/permix.test.ts
@@ -0,0 +1,643 @@
+import 'reflect-metadata'
+import type { INestApplication, Type } from '@nestjs/common'
+import {
+ Controller,
+ ForbiddenException,
+ Get,
+ Module,
+ Post,
+ Req,
+} from '@nestjs/common'
+import { APP_GUARD } from '@nestjs/core'
+import { Test } from '@nestjs/testing'
+import request from 'supertest'
+import { afterEach, describe, expect, it } from 'vitest'
+
+import type { ValidateDefinition } from '../core'
+import { createPermix } from './permix'
+
+interface PostEntity {
+ id: string
+ authorId: string
+}
+
+type PermissionsDefinition = ValidateDefinition<{
+ post: ['create', 'read', 'update']
+ user: ['delete']
+}>
+
+type PostWithData = ValidateDefinition<{
+ post: [{ name: 'create'; type: PostEntity }]
+}>
+
+const denied = {
+ post: { create: false, read: false, update: false },
+ user: { delete: false },
+}
+
+describe('permix/nest', () => {
+ let app: INestApplication | undefined
+
+ afterEach(async () => {
+ if (app) {
+ await app.close()
+ app = undefined
+ }
+ })
+
+ async function createApp(module: Type): Promise {
+ const moduleRef = await Test.createTestingModule({
+ imports: [module],
+ }).compile()
+ app = moduleRef.createNestApplication({ logger: false })
+ await app.init()
+ return app
+ }
+
+ describe(createPermix, () => {
+ const permix = createPermix()
+
+ it('should throw ts error', () => {
+ // @ts-expect-error path does not exist
+ permix.Check('post.delete')
+ })
+
+ it('should allow access when permission is granted', async () => {
+ @Controller()
+ class PostsController {
+ @Post('posts')
+ @permix.Check('post.create')
+ create() {
+ return { success: true }
+ }
+ }
+
+ @Module({
+ controllers: [PostsController],
+ providers: [
+ {
+ provide: APP_GUARD,
+ useValue: permix.guard({
+ post: { create: true, read: false, update: false },
+ user: { delete: false },
+ }),
+ },
+ ],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer())
+ .post('/posts')
+ .send({ title: 'Test Post' })
+
+ expect(response.status).toBe(201)
+ expect(response.body).toStrictEqual({ success: true })
+ })
+
+ it('should deny access when permission is not granted', async () => {
+ @Controller()
+ class PostsController {
+ @Post('posts')
+ @permix.Check('post.create')
+ create() {
+ return { success: true }
+ }
+ }
+
+ @Module({
+ controllers: [PostsController],
+ providers: [{ provide: APP_GUARD, useValue: permix.guard(denied) }],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer())
+ .post('/posts')
+ .send({ title: 'Test Post' })
+
+ expect(response.status).toBe(403)
+ expect(response.body).toStrictEqual({ error: 'Forbidden' })
+ })
+
+ it('should work with custom error handler', async () => {
+ const permix = createPermix({
+ onForbidden: () => {
+ throw new ForbiddenException({ error: 'Custom error' })
+ },
+ })
+
+ @Controller()
+ class PostsController {
+ @Post('posts')
+ @permix.Check('post.create')
+ create() {
+ return { success: true }
+ }
+ }
+
+ @Module({
+ controllers: [PostsController],
+ providers: [{ provide: APP_GUARD, useValue: permix.guard(denied) }],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer())
+ .post('/posts')
+ .send({ title: 'Test Post' })
+
+ expect(response.status).toBe(403)
+ expect(response.body).toStrictEqual({ error: 'Custom error' })
+ })
+
+ it('should work with custom error and params', async () => {
+ const permix = createPermix({
+ onForbidden: ({ path }) => {
+ throw new ForbiddenException({
+ error: `You do not have permission for ${path}`,
+ })
+ },
+ })
+
+ @Controller()
+ class PostsController {
+ @Post('posts')
+ @permix.Check('post.create')
+ create() {
+ return { success: true }
+ }
+ }
+
+ @Module({
+ controllers: [PostsController],
+ providers: [{ provide: APP_GUARD, useValue: permix.guard(denied) }],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer())
+ .post('/posts')
+ .send({ title: 'Test Post' })
+
+ expect(response.status).toBe(403)
+ expect(response.body).toStrictEqual({
+ error: 'You do not have permission for post.create',
+ })
+ })
+
+ it('should pass data through to a rule callback', async () => {
+ const permix = createPermix()
+
+ @Controller()
+ class PostsController {
+ @Post('posts')
+ @permix.Check('post.create', { id: 'a', authorId: '1' })
+ create() {
+ return { success: true }
+ }
+ }
+
+ @Module({
+ controllers: [PostsController],
+ providers: [
+ {
+ provide: APP_GUARD,
+ useValue: permix.guard({
+ post: {
+ create: (post) => post?.authorId === '1',
+ },
+ }),
+ },
+ ],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer())
+ .post('/posts')
+ .send({ title: 'Test Post' })
+
+ expect(response.status).toBe(201)
+ expect(response.body).toStrictEqual({ success: true })
+ })
+
+ it('should work with checker callback form', async () => {
+ const permix = createPermix()
+
+ @Controller()
+ class PostsController {
+ @Post('posts')
+ @permix.Check((c) => c('post.create') && c('user.delete'))
+ create() {
+ return { success: true }
+ }
+ }
+
+ @Module({
+ controllers: [PostsController],
+ providers: [
+ {
+ provide: APP_GUARD,
+ useValue: permix.guard({
+ post: { create: true, read: true, update: false },
+ user: { delete: true },
+ }),
+ },
+ ],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer())
+ .post('/posts')
+ .send({ title: 'Test Post' })
+
+ expect(response.status).toBe(201)
+ expect(response.body).toStrictEqual({ success: true })
+ })
+
+ it('should work with template', async () => {
+ const template = permix.template({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ })
+
+ @Controller()
+ class PostsController {
+ @Post('posts')
+ @permix.Check('post.create')
+ create() {
+ return { success: true }
+ }
+ }
+
+ @Module({
+ controllers: [PostsController],
+ providers: [
+ { provide: APP_GUARD, useValue: permix.guard(() => template()) },
+ ],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer())
+ .post('/posts')
+ .send({ title: 'Test Post' })
+
+ expect(response.status).toBe(201)
+ expect(response.body).toStrictEqual({ success: true })
+ })
+
+ it('should dehydrate permissions', async () => {
+ const template = permix.template({
+ post: { create: true, read: false, update: true },
+ user: { delete: false },
+ })
+
+ @Controller()
+ class DehydrateController {
+ @Get('dehydrate')
+ dehydrate(@Req() req: { [key: PropertyKey]: unknown }) {
+ return permix.getOrThrow(req).dehydrate()
+ }
+ }
+
+ @Module({
+ controllers: [DehydrateController],
+ providers: [
+ { provide: APP_GUARD, useValue: permix.guard(() => template()) },
+ ],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer()).get('/dehydrate')
+
+ expect(response.status).toBe(200)
+ expect(response.body).toStrictEqual({
+ post: { create: true, read: false, update: true },
+ user: { delete: false },
+ })
+ })
+
+ it('should allow a handler without Check after setup', async () => {
+ @Controller()
+ class OpenController {
+ @Get('open')
+ open() {
+ return { ok: true }
+ }
+ }
+
+ @Module({
+ controllers: [OpenController],
+ providers: [{ provide: APP_GUARD, useValue: permix.guard(denied) }],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer()).get('/open')
+ expect(response.status).toBe(200)
+ expect(response.body).toStrictEqual({ ok: true })
+ })
+
+ it('should let two factories with different keys coexist on the same request', async () => {
+ const admin = createPermix().contextKey('admin')
+ const guest = createPermix().contextKey('guest')
+
+ @Controller()
+ class DualController {
+ @Post('admin')
+ @admin.Check('post.create')
+ adminRoute() {
+ return { scope: 'admin' }
+ }
+
+ @Post('guest')
+ @guest.Check('post.create')
+ guestRoute() {
+ return { scope: 'guest' }
+ }
+ }
+
+ @Module({
+ controllers: [DualController],
+ providers: [
+ {
+ provide: APP_GUARD,
+ useValue: admin.guard({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ }),
+ },
+ {
+ provide: APP_GUARD,
+ useValue: guest.guard({
+ post: { create: false, read: true, update: false },
+ user: { delete: false },
+ }),
+ },
+ ],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+
+ const adminResponse = await request(nestApp.getHttpServer()).post(
+ '/admin'
+ )
+ expect(adminResponse.status).toBe(201)
+ expect(adminResponse.body).toStrictEqual({ scope: 'admin' })
+
+ const guestResponse = await request(nestApp.getHttpServer()).post(
+ '/guest'
+ )
+ expect(guestResponse.status).toBe(403)
+ expect(guestResponse.body).toStrictEqual({ error: 'Forbidden' })
+ })
+
+ it('should default to a per-instance symbol so two factories without a key do not collide', async () => {
+ const first = createPermix()
+ const second = createPermix()
+
+ @Controller()
+ class DualController {
+ @Post('first')
+ @first.Check('post.create')
+ firstRoute() {
+ return { ok: true }
+ }
+
+ @Post('second')
+ @second.Check('post.create')
+ secondRoute() {
+ return { ok: true }
+ }
+ }
+
+ @Module({
+ controllers: [DualController],
+ providers: [
+ {
+ provide: APP_GUARD,
+ useValue: first.guard({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ }),
+ },
+ {
+ provide: APP_GUARD,
+ useValue: second.guard({
+ post: { create: false, read: false, update: false },
+ user: { delete: false },
+ }),
+ },
+ ],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+
+ const firstResponse = await request(nestApp.getHttpServer()).post(
+ '/first'
+ )
+ expect(firstResponse.status).toBe(201)
+
+ const secondResponse = await request(nestApp.getHttpServer()).post(
+ '/second'
+ )
+ expect(secondResponse.status).toBe(403)
+ })
+
+ it('should accept an explicit symbol key', async () => {
+ const key = Symbol('my-permix')
+ const permix = createPermix().contextKey(key)
+
+ @Controller()
+ class ProbeController {
+ @Get('probe')
+ probe(@Req() req: { [key: PropertyKey]: unknown }) {
+ return { attached: Boolean(req[key]) }
+ }
+ }
+
+ @Module({
+ controllers: [ProbeController],
+ providers: [
+ {
+ provide: APP_GUARD,
+ useValue: permix.guard({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ }),
+ },
+ ],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer()).get('/probe')
+ expect(response.status).toBe(200)
+ expect(response.body).toStrictEqual({ attached: true })
+ })
+
+ it('should honor a class-level Check decorator', async () => {
+ @Controller('posts')
+ @permix.Check('post.create')
+ class PostsController {
+ @Post()
+ create() {
+ return { success: true }
+ }
+ }
+
+ @Module({
+ controllers: [PostsController],
+ providers: [{ provide: APP_GUARD, useValue: permix.guard(denied) }],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer()).post('/posts')
+ expect(response.status).toBe(403)
+ expect(response.body).toStrictEqual({ error: 'Forbidden' })
+ })
+ })
+
+ describe('get / getOrThrow', () => {
+ const permix = createPermix()
+
+ it('should return null when the guard has not run', async () => {
+ @Controller()
+ class RootController {
+ @Get()
+ root(@Req() req: { [key: PropertyKey]: unknown }) {
+ return { result: permix.get(req) }
+ }
+ }
+
+ @Module({ controllers: [RootController] })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer()).get('/')
+ expect(response.status).toBe(200)
+ expect(response.body).toStrictEqual({ result: null })
+ })
+
+ it('should return the instance when the guard has run', async () => {
+ @Controller()
+ class RootController {
+ @Get()
+ root(@Req() req: { [key: PropertyKey]: unknown }) {
+ const p = permix.getOrThrow(req)
+ return { hasCheck: typeof p.check === 'function' }
+ }
+ }
+
+ @Module({
+ controllers: [RootController],
+ providers: [
+ {
+ provide: APP_GUARD,
+ useValue: permix.guard({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ }),
+ },
+ ],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer()).get('/')
+ expect(response.status).toBe(200)
+ expect(response.body).toStrictEqual({ hasCheck: true })
+ })
+
+ it('getOrThrow should throw PermixNotFoundError when missing', async () => {
+ @Controller()
+ class RootController {
+ @Get()
+ root(@Req() req: { [key: PropertyKey]: unknown }) {
+ permix.getOrThrow(req)
+ return { ok: true }
+ }
+ }
+
+ @Module({ controllers: [RootController] })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer()).get('/')
+ expect(response.status).toBe(500)
+ expect(response.body.statusCode).toBe(500)
+ })
+
+ it('getRules should return null when the guard has not run', async () => {
+ @Controller()
+ class RootController {
+ @Get()
+ root(@Req() req: { [key: PropertyKey]: unknown }) {
+ return { rules: permix.getRules(req) }
+ }
+ }
+
+ @Module({ controllers: [RootController] })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer()).get('/')
+ expect(response.status).toBe(200)
+ expect(response.body).toStrictEqual({ rules: null })
+ })
+
+ it('getRules should return the current rules when the guard has run', async () => {
+ @Controller()
+ class RootController {
+ @Get()
+ root(@Req() req: { [key: PropertyKey]: unknown }) {
+ return { rules: permix.getRules(req) }
+ }
+ }
+
+ @Module({
+ controllers: [RootController],
+ providers: [
+ {
+ provide: APP_GUARD,
+ useValue: permix.guard({
+ post: { create: true, read: false, update: false },
+ user: { delete: true },
+ }),
+ },
+ ],
+ })
+ class AppModule {}
+
+ const nestApp = await createApp(AppModule)
+ const response = await request(nestApp.getHttpServer()).get('/')
+ expect(response.status).toBe(200)
+ expect(response.body).toStrictEqual({
+ rules: {
+ post: { create: true, read: false, update: false },
+ user: { delete: true },
+ },
+ })
+ })
+ })
+
+ describe('key exposure', () => {
+ it('should expose the key on the factory return', () => {
+ const permix =
+ createPermix().contextKey('custom-key')
+ expect(permix.key).toBe('custom-key')
+ })
+
+ it('should expose a symbol key when using default', () => {
+ const permix = createPermix()
+ expect(permix.key).toBeTypeOf('symbol')
+ })
+ })
+})
diff --git a/permix/src/nest/permix.ts b/permix/src/nest/permix.ts
new file mode 100644
index 00000000..7d1f8bc1
--- /dev/null
+++ b/permix/src/nest/permix.ts
@@ -0,0 +1,214 @@
+import type {
+ CanActivate,
+ CustomDecorator,
+ ExecutionContext,
+} from '@nestjs/common'
+import { ForbiddenException, SetMetadata } from '@nestjs/common'
+
+import type { Permix as PermixCore } from '../core'
+import {
+ createCheckContext,
+ createHooks,
+ createPermix as createPermixCore,
+ createTemplate,
+ PermixNotFoundError,
+} from '../core'
+import type { CheckArgs, CheckContext } from '../core/check'
+import type { Definition } from '../core/definitions'
+import type { PermixHooks, Rules, RulesPaths } from '../core/permix'
+import type { MaybePromise } from '../utils'
+
+/**
+ * HTTP request object from `ExecutionContext.switchToHttp().getRequest()`.
+ * Compatible with both the Express and Fastify Nest adapters.
+ */
+export type NestHttpRequest = Record
+
+export interface GuardContext {
+ req: NestHttpRequest
+ context: ExecutionContext
+}
+
+export interface PermixOptions {
+ /**
+ * Called when a `@Check` decorator denies the request. Defaults to throwing
+ * a Nest `ForbiddenException` with `{ error: 'Forbidden' }`.
+ */
+ onForbidden?: (params: CheckContext & GuardContext) => MaybePromise
+}
+
+function getRequest(context: ExecutionContext): NestHttpRequest {
+ return context.switchToHttp().getRequest()
+}
+
+function readCheckArgs(
+ metadataKey: string | symbol,
+ context: ExecutionContext
+): CheckArgs | undefined {
+ const handler = context.getHandler()
+ const classRef = context.getClass()
+ const fromHandler = Reflect.getMetadata(metadataKey, handler) as
+ | CheckArgs
+ | undefined
+ if (fromHandler) {
+ return fromHandler
+ }
+ return Reflect.getMetadata(metadataKey, classRef) as CheckArgs | undefined
+}
+
+function buildPermix(
+ resolveKey: () => string | symbol,
+ options: PermixOptions = {}
+) {
+ const checkMetadataKey = Symbol('permix:check')
+ const onForbidden =
+ options.onForbidden ??
+ (() => {
+ throw new ForbiddenException({ error: 'Forbidden' })
+ })
+
+ const hooks = createHooks>()
+
+ function get(req: NestHttpRequest): PermixCore | null {
+ const instance = req[resolveKey()] as PermixCore | undefined
+ return instance ?? null
+ }
+
+ function getOrThrow(req: NestHttpRequest): PermixCore {
+ const instance = get(req)
+ if (!instance) {
+ throw new PermixNotFoundError(resolveKey())
+ }
+ return instance
+ }
+
+ function attach(req: NestHttpRequest, rules: Rules): PermixCore {
+ const instance = createPermixCore(rules)
+ instance.hook('check', (context) => {
+ hooks.callHook('check', context)
+ })
+ req[resolveKey()] = instance
+ return instance
+ }
+
+ /**
+ * Nest guard that always sets up a per-request Permix instance, then enforces
+ * `@Check(...)` when that decorator is present on the handler or controller.
+ *
+ * Register globally with `APP_GUARD`, or per-controller / per-route with
+ * `@UseGuards`.
+ */
+ function guard(
+ callbackOrRules:
+ | ((context: GuardContext) => MaybePromise>)
+ | Rules
+ ): CanActivate {
+ return {
+ async canActivate(context) {
+ const req = getRequest(context)
+ const rules =
+ typeof callbackOrRules === 'function'
+ ? await callbackOrRules({ req, context })
+ : callbackOrRules
+ const instance = attach(req, rules)
+
+ const args = readCheckArgs(checkMetadataKey, context)
+ if (!args) {
+ return true
+ }
+
+ const allowed = instance.check(...args)
+ if (allowed) {
+ return true
+ }
+
+ await onForbidden({
+ req,
+ context,
+ ...createCheckContext(...args),
+ })
+ return false
+ },
+ }
+ }
+
+ /**
+ * Method or class decorator that records the permission check for `guard()`.
+ */
+ const Check: (...args: CheckArgs) => CustomDecorator = (
+ ...args
+ ) => SetMetadata(checkMetadataKey, args)
+
+ function getRules(req: NestHttpRequest): Rules | null {
+ return get(req)?.getRules() ?? null
+ }
+
+ function template(rules: Rules | ((param: T) => Rules)) {
+ return createTemplate(rules)
+ }
+
+ return {
+ guard,
+ Check,
+ template,
+ get,
+ getOrThrow,
+ getRules,
+ hook: hooks.hook,
+ hookOnce: hooks.hookOnce,
+ get key() {
+ return resolveKey()
+ },
+ $inferDefinition: undefined as unknown as D,
+ $inferPath: undefined as unknown as RulesPaths,
+ }
+}
+
+/**
+ * Create a guard factory that wires Permix into NestJS routes.
+ *
+ * Use `.contextKey('name')` to set a custom request key (defaults to a unique
+ * `Symbol('permix')`).
+ *
+ * @example
+ * ```ts
+ * import { APP_GUARD } from '@nestjs/core'
+ * import { createPermix } from 'permix/nest'
+ *
+ * const permix = createPermix<{
+ * post: ['create', 'read']
+ * }>()
+ *
+ * @Get()
+ * @permix.Check('post.read')
+ * findAll() {}
+ *
+ * // app.module.ts
+ * {
+ * provide: APP_GUARD,
+ * useValue: permix.guard(({ req }) => ({
+ * post: { create: !!req.user, read: true },
+ * })),
+ * }
+ * ```
+ *
+ * @link https://permix.letstri.dev/docs/integrations/nest
+ */
+export function createPermix(
+ options: PermixOptions = {}
+) {
+ let key: string | symbol = Symbol('permix')
+ const permix = buildPermix(() => key, options)
+
+ return Object.assign(permix, {
+ contextKey(newKey: string | symbol) {
+ key = newKey
+ return permix
+ },
+ })
+}
+
+/** Return type of {@link createPermix}. */
+export type NestPermix = ReturnType<
+ typeof createPermix
+>
diff --git a/permix/tsconfig.base.json b/permix/tsconfig.base.json
index cf0229e7..d24217e3 100644
--- a/permix/tsconfig.base.json
+++ b/permix/tsconfig.base.json
@@ -7,7 +7,8 @@
"strict": true,
"noEmit": true,
"forceConsistentCasingInFileNames": true,
- "skipLibCheck": true
+ "skipLibCheck": true,
+ "experimentalDecorators": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "*.ts"],
"exclude": [
diff --git a/permix/tsdown.config.ts b/permix/tsdown.config.ts
index 00e9a0a3..1a2cdb10 100644
--- a/permix/tsdown.config.ts
+++ b/permix/tsdown.config.ts
@@ -22,6 +22,7 @@ export default defineConfig({
'./src/drizzle/legacy/index.ts',
'./src/next/index.ts',
'./src/tanstack-start/index.ts',
+ './src/nest/index.ts',
],
dts: {
build: true,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 761d130a..3b37f901 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -263,6 +263,34 @@ importers:
specifier: ^8.0.16
version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ examples/nest:
+ dependencies:
+ '@nestjs/common':
+ specifier: ^11.1.6
+ version: 11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/core':
+ specifier: ^11.1.6
+ version: 11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/platform-express':
+ specifier: ^11.1.6
+ version: 11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.3)
+ permix:
+ specifier: workspace:*
+ version: link:../../permix
+ reflect-metadata:
+ specifier: ^0.2.2
+ version: 0.2.2
+ rxjs:
+ specifier: ^7.8.2
+ version: 7.8.2
+ devDependencies:
+ tsx:
+ specifier: ^4.22.4
+ version: 4.22.4
+ typescript:
+ specifier: ^6.0.3
+ version: 6.0.3
+
examples/next:
dependencies:
next:
@@ -435,7 +463,7 @@ importers:
version: 1.167.1(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-router-ssr-query':
specifier: latest
- version: 1.167.1(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ version: 1.167.2(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start':
specifier: latest
version: 1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -556,6 +584,18 @@ importers:
specifier: '>=1'
version: 1.9.13
devDependencies:
+ '@nestjs/common':
+ specifier: ^11.2.3
+ version: 11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/core':
+ specifier: ^11.2.3
+ version: 11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/platform-express':
+ specifier: ^11.2.3
+ version: 11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.3)
+ '@nestjs/testing':
+ specifier: ^11.2.3
+ version: 11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.3)(@nestjs/platform-express@11.2.3)
'@solidjs/testing-library':
specifier: ^0.8.10
version: 0.8.10(solid-js@1.9.13)
@@ -613,6 +653,12 @@ importers:
react-dom:
specifier: ^19.2.6
version: 19.2.6(react@19.2.6)
+ reflect-metadata:
+ specifier: ^0.2.2
+ version: 0.2.2
+ rxjs:
+ specifier: ^7.8.2
+ version: 7.8.2
supertest:
specifier: ^7.2.2
version: 7.2.2
@@ -621,7 +667,7 @@ importers:
version: 5.56.0
svelte-check:
specifier: ^4.5.0
- version: 4.5.0(picomatch@4.0.7)(svelte@5.56.0)(typescript@6.0.3)
+ version: 4.5.0(picomatch@4.0.4)(svelte@5.56.0)(typescript@6.0.3)
tsdown:
specifier: ^0.22.1
version: 0.22.1(tsx@4.22.4)(typescript@6.0.3)
@@ -1252,6 +1298,10 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+ '@lukeed/csprng@1.1.0':
+ resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==}
+ engines: {node: '>=8'}
+
'@mdx-js/mdx@3.1.1':
resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==}
@@ -1273,6 +1323,56 @@ packages:
'@neodrag/core': 3.0.0-next.11
solid-js: ^1.0.0
+ '@nestjs/common@11.2.3':
+ resolution: {integrity: sha512-obdauJXHfthhepbV+LpGe88OeBlR/Kw9lwjLo0Utzc//agoLXYb9DUGhPQWtm81IpBWMv+19eiwcve9MsBZwXA==}
+ peerDependencies:
+ class-transformer: '>=0.4.1'
+ class-validator: '>=0.13.2'
+ reflect-metadata: ^0.1.12 || ^0.2.0
+ rxjs: ^7.1.0
+ peerDependenciesMeta:
+ class-transformer:
+ optional: true
+ class-validator:
+ optional: true
+
+ '@nestjs/core@11.2.3':
+ resolution: {integrity: sha512-vkA9/Ja0Z3hvqXErSa+HaxrfF+cNXthNFi8VPNEKVli4rMd009yExAl0gLmko/Kf8peDXr72u1RN+j9Da2ukHg==}
+ engines: {node: '>= 20'}
+ peerDependencies:
+ '@nestjs/common': ^11.0.0
+ '@nestjs/microservices': ^11.0.0
+ '@nestjs/platform-express': ^11.0.0
+ '@nestjs/websockets': ^11.0.0
+ reflect-metadata: ^0.1.12 || ^0.2.0
+ rxjs: ^7.1.0
+ peerDependenciesMeta:
+ '@nestjs/microservices':
+ optional: true
+ '@nestjs/platform-express':
+ optional: true
+ '@nestjs/websockets':
+ optional: true
+
+ '@nestjs/platform-express@11.2.3':
+ resolution: {integrity: sha512-YFQvRXT2de1qNL9LJPUBQ31+RsfI4cJ+sbpU9ENM/hDCgoHSEhm7oxUuGGKmhTZBNZEYm8mDYdfoTFmAH1LIJg==}
+ peerDependencies:
+ '@nestjs/common': ^11.0.0
+ '@nestjs/core': ^11.0.0
+
+ '@nestjs/testing@11.2.3':
+ resolution: {integrity: sha512-7ANDWlkm8Xw4CYIhCNZhtBzANsQUKqjteA2yx/6sjqGyWhekeBKz8wgCJykm0vo+ltrg6U34dZlm2NgiRcNHPQ==}
+ peerDependencies:
+ '@nestjs/common': ^11.0.0
+ '@nestjs/core': ^11.0.0
+ '@nestjs/microservices': ^11.0.0
+ '@nestjs/platform-express': ^11.0.0
+ peerDependenciesMeta:
+ '@nestjs/microservices':
+ optional: true
+ '@nestjs/platform-express':
+ optional: true
+
'@next/env@16.2.6':
resolution: {integrity: sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==}
@@ -2634,12 +2734,12 @@ packages:
'@tanstack/router-core':
optional: true
- '@tanstack/react-router-ssr-query@1.167.1':
- resolution: {integrity: sha512-W9j5JPnBikyafvuUfykFfHIWod58OAbAAa5leNkXBcoDoocghMmu6w9uZOmUZvAWT7CSvgj5tBUtF7CM2OoHXQ==}
+ '@tanstack/react-router-ssr-query@1.167.2':
+ resolution: {integrity: sha512-yRvy0VJ00R8huPuquk+qyYQGMpYRc/G33mc6/yZ4f7LaBZz9h/VbACjmLzhm/+6u5WSmUt6ouJJZnI1/5Npfag==}
engines: {node: '>=20.19'}
peerDependencies:
- '@tanstack/query-core': '>=5.90.0'
- '@tanstack/react-query': '>=5.90.0'
+ '@tanstack/query-core': '>=5.102.0'
+ '@tanstack/react-query': '>=5.102.0'
'@tanstack/react-router': '>=1.127.0'
react: '>=18.0.0 || >=19.0.0'
react-dom: '>=18.0.0 || >=19.0.0'
@@ -2927,11 +3027,11 @@ packages:
webpack:
optional: true
- '@tanstack/router-ssr-query-core@1.169.1':
- resolution: {integrity: sha512-rngux8s/3mPQzcjLYDLkNU31coYVyCgrVTfpdwqUdY5jIEHqGTXrO73DTkPR1PppwYUeVhmNCgl8TctRcnupjg==}
+ '@tanstack/router-ssr-query-core@1.169.2':
+ resolution: {integrity: sha512-7pO65Aiq/1+aS3Mb6vSGtIjzQ/YGv9JTfBbn2EYlHcZnJ4s4ZGCeTPI847geB1RfJqVhfwxM8bZgGM51mYkolg==}
engines: {node: '>=20.19'}
peerDependencies:
- '@tanstack/query-core': '>=5.90.0'
+ '@tanstack/query-core': '>=5.102.0'
'@tanstack/router-core': '>=1.127.0'
'@tanstack/router-utils@1.162.1':
@@ -3568,6 +3668,9 @@ packages:
resolution: {integrity: sha512-44mvgtPvohuU/70DdY5Oz2AIrLJ9k6/5x4KmoSvPwO+5Moijo0+N9D0fKbbYZQWP1hNm5CpOf+E01jhxG/r8xg==}
engines: {node: '>=14'}
+ append-field@1.0.0:
+ resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==}
+
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -3668,6 +3771,13 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
+ buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+ busboy@1.6.0:
+ resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
+ engines: {node: '>=10.16.0'}
+
bytes@3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
@@ -3786,6 +3896,10 @@ packages:
compute-scroll-into-view@3.1.1:
resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==}
+ concat-stream@2.0.0:
+ resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
+ engines: {'0': node >= 6.0}
+
confbox@0.1.8:
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
@@ -4514,6 +4628,10 @@ packages:
resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
engines: {node: '>=18'}
+ file-type@21.3.4:
+ resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==}
+ engines: {node: '>=20'}
+
file-type@22.0.1:
resolution: {integrity: sha512-ww5Mhre0EE+jmBvOXTmXAbEMuZE7uX4a3+oRCQFNj8w++g3ev913N6tXQz0XTXbueQ5TWQfm6BdaViEHHn8bhA==}
engines: {node: '>=22'}
@@ -5000,6 +5118,10 @@ packages:
resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==}
engines: {node: '>=8'}
+ iterare@1.2.1:
+ resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==}
+ engines: {node: '>=6'}
+
jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
@@ -5154,6 +5276,10 @@ packages:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
+ load-esm@1.0.3:
+ resolution: {integrity: sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==}
+ engines: {node: '>=13.2.0'}
+
locate-character@3.0.0:
resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
@@ -5271,6 +5397,10 @@ packages:
mdn-data@2.27.1:
resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
+ media-typer@0.3.0:
+ resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
+ engines: {node: '>= 0.6'}
+
media-typer@1.1.0:
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
engines: {node: '>= 0.8'}
@@ -5484,6 +5614,10 @@ packages:
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+ multer@2.2.0:
+ resolution: {integrity: sha512-6rdyFg2kLrMh9Jee7/BMPuV9lEAd7lLW2YUpF9/YxR7njyoUwwQ0ZPh3TaIY50Sw6vlyD2HW3wGOkTS4P79xrQ==}
+ engines: {node: '>= 10.16.0'}
+
nanoid@3.3.12:
resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -5909,6 +6043,10 @@ packages:
resolution: {integrity: sha512-PNaGjoCnw9DBA2Kl8D+8po957z778q/HOPuY2u3Bkw/JO3eC8MDx7jn/PgMtSgpcBbs+6UOjDbwReGpXmRvs0g==}
engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0}
+ readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+
readdirp@4.1.2:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
@@ -5942,6 +6080,9 @@ packages:
resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
engines: {node: '>=8'}
+ reflect-metadata@0.2.2:
+ resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==}
+
regex-recursion@6.0.2:
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
@@ -6048,10 +6189,16 @@ packages:
rw@1.3.3:
resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
+ rxjs@7.8.2:
+ resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
+
sade@1.8.1:
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
engines: {node: '>=6'}
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
safe-regex2@5.1.1:
resolution: {integrity: sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==}
hasBin: true
@@ -6220,6 +6367,10 @@ packages:
std-env@4.1.0:
resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==}
+ streamsearch@1.1.0:
+ resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
+ engines: {node: '>=10.0.0'}
+
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@@ -6232,6 +6383,9 @@ packages:
resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==}
engines: {node: '>=20'}
+ string_decoder@1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+
stringify-entities@4.0.4:
resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
@@ -6473,10 +6627,17 @@ packages:
resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==}
engines: {node: '>=20'}
+ type-is@1.6.18:
+ resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
+ engines: {node: '>= 0.6'}
+
type-is@2.1.0:
resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==}
engines: {node: '>= 18'}
+ typedarray@0.0.6:
+ resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
+
typescript@6.0.3:
resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
engines: {node: '>=14.17'}
@@ -6485,6 +6646,10 @@ packages:
ufo@1.6.4:
resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==}
+ uid@2.0.2:
+ resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
+ engines: {node: '>=8'}
+
uint8array-extras@1.5.0:
resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==}
engines: {node: '>=18'}
@@ -7449,6 +7614,8 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
+ '@lukeed/csprng@1.1.0': {}
+
'@mdx-js/mdx@3.1.1':
dependencies:
'@types/estree': 1.0.9
@@ -7497,6 +7664,51 @@ snapshots:
'@neodrag/core': 3.0.0-next.11
solid-js: 1.9.13
+ '@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2)':
+ dependencies:
+ file-type: 21.3.4
+ iterare: 1.2.1
+ load-esm: 1.0.3
+ reflect-metadata: 0.2.2
+ rxjs: 7.8.2
+ tslib: 2.8.1
+ uid: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@nestjs/core@11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
+ dependencies:
+ '@nestjs/common': 11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ fast-safe-stringify: 2.1.1
+ iterare: 1.2.1
+ path-to-regexp: 8.4.2
+ reflect-metadata: 0.2.2
+ rxjs: 7.8.2
+ tslib: 2.8.1
+ uid: 2.0.2
+ optionalDependencies:
+ '@nestjs/platform-express': 11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.3)
+
+ '@nestjs/platform-express@11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.3)':
+ dependencies:
+ '@nestjs/common': 11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/core': 11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ cors: 2.8.6
+ express: 5.2.1
+ multer: 2.2.0
+ path-to-regexp: 8.4.2
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@nestjs/testing@11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.3)(@nestjs/platform-express@11.2.3)':
+ dependencies:
+ '@nestjs/common': 11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/core': 11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.2.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ tslib: 2.8.1
+ optionalDependencies:
+ '@nestjs/platform-express': 11.2.3(@nestjs/common@11.2.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.2.3)
+
'@next/env@16.2.6': {}
'@next/swc-darwin-arm64@16.2.6':
@@ -8665,12 +8877,12 @@ snapshots:
transitivePeerDependencies:
- csstype
- '@tanstack/react-router-ssr-query@1.167.1(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
+ '@tanstack/react-router-ssr-query@1.167.2(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
'@tanstack/query-core': 5.101.0
'@tanstack/react-query': 5.101.0(react@19.2.6)
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/router-ssr-query-core': 1.169.1(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)
+ '@tanstack/router-ssr-query-core': 1.169.2(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
transitivePeerDependencies:
@@ -9062,7 +9274,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tanstack/router-ssr-query-core@1.169.1(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)':
+ '@tanstack/router-ssr-query-core@1.169.2(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)':
dependencies:
'@tanstack/query-core': 5.101.0
'@tanstack/router-core': 1.171.27
@@ -9862,6 +10074,8 @@ snapshots:
ansis@4.3.0: {}
+ append-field@1.0.0: {}
+
argparse@2.0.1: {}
aria-hidden@1.2.6:
@@ -9972,6 +10186,12 @@ snapshots:
node-releases: 2.0.46
update-browserslist-db: 1.2.3(browserslist@4.28.2)
+ buffer-from@1.1.2: {}
+
+ busboy@1.6.0:
+ dependencies:
+ streamsearch: 1.1.0
+
bytes@3.1.2: {}
cac@6.7.14: {}
@@ -10063,6 +10283,13 @@ snapshots:
compute-scroll-into-view@3.1.1: {}
+ concat-stream@2.0.0:
+ dependencies:
+ buffer-from: 1.1.2
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+ typedarray: 0.0.6
+
confbox@0.1.8: {}
config-chain@1.1.13:
@@ -10708,6 +10935,15 @@ snapshots:
dependencies:
is-unicode-supported: 2.1.0
+ file-type@21.3.4:
+ dependencies:
+ '@tokenizer/inflate': 0.4.1
+ strtok3: 10.3.5
+ token-types: 6.1.2
+ uint8array-extras: 1.5.0
+ transitivePeerDependencies:
+ - supports-color
+
file-type@22.0.1:
dependencies:
'@tokenizer/inflate': 0.4.1
@@ -11246,6 +11482,8 @@ snapshots:
html-escaper: 2.0.2
istanbul-lib-report: 3.0.1
+ iterare@1.2.1: {}
+
jackspeak@3.4.3:
dependencies:
'@isaacs/cliui': 8.0.2
@@ -11385,6 +11623,8 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
+ load-esm@1.0.3: {}
+
locate-character@3.0.0: {}
lodash-es@4.18.1: {}
@@ -11611,6 +11851,8 @@ snapshots:
mdn-data@2.27.1: {}
+ media-typer@0.3.0: {}
+
media-typer@1.1.0: {}
memoirist@0.4.0: {}
@@ -11973,6 +12215,13 @@ snapshots:
muggle-string@0.4.1: {}
+ multer@2.2.0:
+ dependencies:
+ append-field: 1.0.0
+ busboy: 1.6.0
+ concat-stream: 2.0.0
+ type-is: 1.6.18
+
nanoid@3.3.12: {}
negotiator@1.0.0: {}
@@ -12039,7 +12288,7 @@ snapshots:
postcss: 8.4.31
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.6)
+ styled-jsx: 5.1.6(react@19.2.6)
optionalDependencies:
'@next/swc-darwin-arm64': 16.2.6
'@next/swc-darwin-x64': 16.2.6
@@ -12495,6 +12744,12 @@ snapshots:
json-parse-even-better-errors: 6.0.0
npm-normalize-package-bin: 6.0.0
+ readable-stream@3.6.2:
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+
readdirp@4.1.2: {}
readdirp@5.0.0: {}
@@ -12537,6 +12792,8 @@ snapshots:
indent-string: 4.0.0
strip-indent: 3.0.0
+ reflect-metadata@0.2.2: {}
+
regex-recursion@6.0.2:
dependencies:
regex-utilities: 2.3.0
@@ -12694,10 +12951,16 @@ snapshots:
rw@1.3.3: {}
+ rxjs@7.8.2:
+ dependencies:
+ tslib: 2.8.1
+
sade@1.8.1:
dependencies:
mri: 1.2.0
+ safe-buffer@5.2.1: {}
+
safe-regex2@5.1.1:
dependencies:
ret: 0.5.0
@@ -12898,6 +13161,8 @@ snapshots:
std-env@4.1.0: {}
+ streamsearch@1.1.0: {}
+
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
@@ -12915,6 +13180,10 @@ snapshots:
get-east-asian-width: 1.6.0
strip-ansi: 7.2.0
+ string_decoder@1.3.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
stringify-entities@4.0.4:
dependencies:
character-entities-html4: 2.1.0
@@ -12958,6 +13227,12 @@ snapshots:
client-only: 0.0.1
react: 19.2.4
+ styled-jsx@5.1.6(react@19.2.6):
+ dependencies:
+ client-only: 0.0.1
+ react: 19.2.6
+ optional: true
+
stylis@4.4.0: {}
superagent@10.3.0:
@@ -12986,6 +13261,18 @@ snapshots:
dependencies:
has-flag: 4.0.0
+ svelte-check@4.5.0(picomatch@4.0.4)(svelte@5.56.0)(typescript@6.0.3):
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ chokidar: 4.0.3
+ fdir: 6.5.0(picomatch@4.0.4)
+ picocolors: 1.1.1
+ sade: 1.8.1
+ svelte: 5.56.0
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - picomatch
+
svelte-check@4.5.0(picomatch@4.0.7)(svelte@5.56.0)(typescript@6.0.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
@@ -13169,16 +13456,27 @@ snapshots:
dependencies:
tagged-tag: 1.0.0
+ type-is@1.6.18:
+ dependencies:
+ media-typer: 0.3.0
+ mime-types: 2.1.35
+
type-is@2.1.0:
dependencies:
content-type: 2.0.0
media-typer: 1.1.0
mime-types: 3.0.2
+ typedarray@0.0.6: {}
+
typescript@6.0.3: {}
ufo@1.6.4: {}
+ uid@2.0.2:
+ dependencies:
+ '@lukeed/csprng': 1.1.0
+
uint8array-extras@1.5.0: {}
ultracite@7.10.6(oxfmt@0.64.0(svelte@5.56.0))(oxlint@1.80.0(oxlint-tsgolint@7.0.2001)):
From b9d8b08c2f3e24cf161c3eb2960058880b1a12a6 Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 14:58:45 +0200
Subject: [PATCH 12/28] feat: add Nuxt adapter with per-request Nitro isolation
Store one Permix instance per Nitro event so Nuxt server routes and Vue SSR share rules without leaking state across requests.
Co-authored-by: Cursor
---
_artifacts/domain_map.yaml | 9 +-
_artifacts/skill_tree.yaml | 5 +-
docs/content/docs/comparison.mdx | 3 +-
docs/content/docs/integrations/next.mdx | 4 +-
docs/content/docs/integrations/nuxt.mdx | 282 ++++++++++++++++++++
docs/content/docs/meta.json | 1 +
docs/content/docs/migration-v3-to-v4.mdx | 1 +
docs/content/docs/quick-start.mdx | 4 +
examples/nuxt/main.ts | 66 +++++
examples/nuxt/package.json | 17 ++
examples/nuxt/tsconfig.json | 11 +
permix/package.json | 10 +
permix/skills/permix/SKILL.md | 5 +-
permix/skills/permix/references/frontend.md | 6 +-
permix/src/nuxt/index.ts | 1 +
permix/src/nuxt/permix.test.ts | 215 +++++++++++++++
permix/src/nuxt/permix.ts | 161 +++++++++++
permix/tsdown.config.ts | 1 +
pnpm-lock.yaml | 129 +++++----
19 files changed, 875 insertions(+), 56 deletions(-)
create mode 100644 docs/content/docs/integrations/nuxt.mdx
create mode 100644 examples/nuxt/main.ts
create mode 100644 examples/nuxt/package.json
create mode 100644 examples/nuxt/tsconfig.json
create mode 100644 permix/src/nuxt/index.ts
create mode 100644 permix/src/nuxt/permix.test.ts
create mode 100644 permix/src/nuxt/permix.ts
diff --git a/_artifacts/domain_map.yaml b/_artifacts/domain_map.yaml
index 318aa7c9..df41d08c 100644
--- a/_artifacts/domain_map.yaml
+++ b/_artifacts/domain_map.yaml
@@ -39,7 +39,7 @@ domains:
- name: 'SSR and hydration'
slug: ssr
description: >
- dehydrate/hydrate snapshots, PermixHydrate, Next.js and TanStack Start wiring.
+ dehydrate/hydrate snapshots, PermixHydrate, Next.js, TanStack Start, and Nuxt wiring.
skills:
- name: 'Getting started'
@@ -77,7 +77,7 @@ skills:
Everything past initial setup: dot-path check, callback combinators,
~all/~any, entity-aware ReBAC rules, isReady/isReadyAsync;
PermixProvider/usePermix/createComponents and SSR dehydrate/hydrate for
- React, Vue, Solid, Svelte, Next.js, TanStack Start; setupMiddleware and
+ React, Vue, Solid, Svelte, Next.js, TanStack Start, Nuxt; setupMiddleware and
checkMiddleware for Express, Hono, Fastify, tRPC, oRPC, Node, Elysia.
Single skill with a thin SKILL.md router and three reference files
loaded on demand.
@@ -91,6 +91,7 @@ skills:
- svelte
- next
- tanstack-start
+ - nuxt
- express
- hono
- fastify
@@ -119,7 +120,7 @@ skills:
- 'Wrap the app tree and hide actions based on permissions'
- 'Integrate permix/react, permix/vue, permix/solid, or permix/svelte'
- 'Pass permission booleans from server render to client'
- - 'Wire permix/next or permix/tanstack-start'
+ - 'Wire permix/next, permix/tanstack-start, or permix/nuxt'
- 'Protect HTTP or RPC routes on the server'
- 'Derive rules from authenticated request context'
references:
@@ -137,6 +138,7 @@ skills:
- 'letstri/permix:docs/content/docs/integrations/svelte.mdx'
- 'letstri/permix:docs/content/docs/integrations/next.mdx'
- 'letstri/permix:docs/content/docs/integrations/tanstack-start.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/nuxt.mdx'
- 'letstri/permix:docs/content/docs/integrations/express.mdx'
- 'letstri/permix:docs/content/docs/integrations/hono.mdx'
- 'letstri/permix:docs/content/docs/integrations/fastify.mdx'
@@ -153,6 +155,7 @@ coverage:
- examples/*
- next
- tanstack-start
+ - nuxt
failure_modes:
- mistake: 'Using v3 { action, dataType } schema shape'
diff --git a/_artifacts/skill_tree.yaml b/_artifacts/skill_tree.yaml
index 70b42866..69efa7db 100644
--- a/_artifacts/skill_tree.yaml
+++ b/_artifacts/skill_tree.yaml
@@ -39,7 +39,7 @@ skills:
check() dot paths, callbacks, ~all/~any, entity-aware ReBAC rules,
isReady/isReadyAsync; PermixProvider/usePermix/createComponents and SSR
dehydrate/hydrate for React, Vue, Solid, Svelte, Next.js, TanStack
- Start; setupMiddleware/checkMiddleware for Express, Hono, Fastify,
+ Start, Nuxt; setupMiddleware/checkMiddleware for Express, Hono, Fastify,
tRPC, oRPC, Node, Elysia. Thin router with references loaded on demand.
requires:
- permix-getting-started
@@ -50,6 +50,7 @@ skills:
- svelte
- next
- tanstack-start
+ - nuxt
- express
- hono
- fastify
@@ -72,6 +73,7 @@ skills:
- 'letstri/permix:docs/content/docs/integrations/svelte.mdx'
- 'letstri/permix:docs/content/docs/integrations/next.mdx'
- 'letstri/permix:docs/content/docs/integrations/tanstack-start.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/nuxt.mdx'
- 'letstri/permix:docs/content/docs/integrations/express.mdx'
- 'letstri/permix:docs/content/docs/integrations/hono.mdx'
- 'letstri/permix:docs/content/docs/integrations/fastify.mdx'
@@ -88,3 +90,4 @@ coverage:
- examples/*
- next
- tanstack-start
+ - nuxt
diff --git a/docs/content/docs/comparison.mdx b/docs/content/docs/comparison.mdx
index d0d9ea67..78198c77 100644
--- a/docs/content/docs/comparison.mdx
+++ b/docs/content/docs/comparison.mdx
@@ -19,7 +19,7 @@ Permix is a library that provides a way to manage permissions in your applicatio
| Events | ✅ | ❌ |
| Simple DX | ✅ Create instance, use built-in integrations | ❌ In CASL you need to manage a lot of stuff manually (type-safe, hydration, etc.) |
| Modernity | ✅ Uses modern updates and features of each lib and framework | ❌ CASL was created a long time ago and hasn't updated the core |
-| Size | **2.64 kB** gzip (core) [react 0.86 kB, vue 0.87 kB, solid 0.82 kB, next 1.00 kB, tanstack-start 2.05 kB, svelte ~2.17 kB, …] | **6.17 kB** min+gzip (core) [@casl/react 0.62 kB] |
+| Size | **2.64 kB** gzip (core) [react 0.86 kB, vue 0.87 kB, solid 0.82 kB, next 1.00 kB, nuxt 0.99 kB, tanstack-start 2.05 kB, svelte ~2.17 kB, …] | **6.17 kB** min+gzip (core) [@casl/react 0.62 kB] |
Sizes are hard numbers from published builds — see [Bundle size](#bundle-size). Bracketed values are integration adapters imported on top of core.
@@ -39,6 +39,7 @@ Built with `pnpm run build` in the `permix` package (`tsdown` for all entries ex
| `permix/solid` | 0.82 kB | adapter |
| `permix/svelte` | ~2.17 kB | adapter (`dist/svelte/`, `svelte-package` build) |
| `permix/next` | 1.00 kB | adapter |
+| `permix/nuxt` | 0.99 kB | adapter |
| `permix/tanstack-start` | 2.05 kB | adapter |
| `permix/node` | 0.95 kB | adapter |
| `permix/server` | 1.10 kB | adapter |
diff --git a/docs/content/docs/integrations/next.mdx b/docs/content/docs/integrations/next.mdx
index 5f1f1204..63e20c53 100644
--- a/docs/content/docs/integrations/next.mdx
+++ b/docs/content/docs/integrations/next.mdx
@@ -322,8 +322,8 @@ Returns an object with the following methods:
## TanStack Start and other frameworks
-The client layer (`permix/react`) is framework-agnostic. If you're using TanStack Start, Remix, or a custom React SSR setup, you can still use `permix/react` on the client. For the server side, either:
+The client layer (`permix/react`) is framework-agnostic. If you're using TanStack Start, Remix, or a custom React SSR setup, you can still use `permix/react` on the client. For Nuxt, use [`permix/nuxt`](/docs/integrations/nuxt) on the server and `permix/vue` on the client. For the server side, either:
-- Use the dedicated [`permix/tanstack-start`](/docs/integrations/tanstack-start) integration, which follows the same shape as `permix/next`, or
+- Use the dedicated [`permix/tanstack-start`](/docs/integrations/tanstack-start) or [`permix/nuxt`](/docs/integrations/nuxt) integration, which follow the same shape as `permix/next`, or
- Create a core Permix instance per request manually (see [Hydration guide](/docs/guide/hydration)), or
- Use an existing server integration like [`permix/node`](/docs/integrations/node), [`permix/express`](/docs/integrations/express), or [`permix/hono`](/docs/integrations/hono) when applicable.
diff --git a/docs/content/docs/integrations/nuxt.mdx b/docs/content/docs/integrations/nuxt.mdx
new file mode 100644
index 00000000..03bdcdc7
--- /dev/null
+++ b/docs/content/docs/integrations/nuxt.mdx
@@ -0,0 +1,282 @@
+---
+title: Nuxt
+description: Learn how to use Permix with Nuxt
+---
+
+## Overview
+
+Permix provides a dedicated integration for [Nuxt](https://nuxt.com/) through `permix/nuxt`. It exposes a `createPermix` factory that returns a **per-request** Permix instance stored on the Nitro `event.context`, so you can `setup()` the rules once and `check()` them in server middleware, API routes, and Vue server components without leaking state between concurrent requests.
+
+The client side reuses the existing Vue integration (`permix/vue`): the server `dehydrate()`s its state and the client hydrates it into its own singleton via `PermixProvider` + `PermixHydrate`.
+
+
+ Before getting started with the Nuxt integration, make sure you've completed
+ the initial setup steps in the [Quick Start](/docs/quick-start) guide.
+ Familiarity with the [Hydration guide](/docs/guide/hydration) and the [Vue
+ integration](/docs/integrations/vue) helps too.
+
+
+
+
+
+
+## Define your permissions
+
+Create a Permix instance once in a shared module so it can be imported from Nitro and from Vue:
+
+```ts title="lib/permix.ts"
+import { createPermix } from 'permix/nuxt'
+
+interface Post {
+ id: string
+ authorId: string
+}
+
+export const permix = createPermix<{
+ post: [
+ { name: 'create'; type: Post },
+ { name: 'read'; type: Post },
+ { name: 'update'; type: Post },
+ { name: 'delete'; type: Post },
+ ]
+}>()
+```
+
+The returned helper does **not** hold any permission state at module scope — every request gets its own isolated instance on `event.context`.
+
+
+
+
+
+## Setup per request
+
+Call `setup()` early in the request lifecycle. A Nitro server middleware is a good place. Resolve any async data (session, headers, DB lookups) first, then pass plain rules to `setup`. Pass the `event` so the instance is stored on this request:
+
+```ts title="server/middleware/permix.ts"
+import { permix } from '~/lib/permix'
+
+export default defineEventHandler((event) => {
+ const user = event.context.user
+
+ permix.setup(
+ {
+ post: {
+ create: !!user,
+ read: true,
+ update: (post) => post?.authorId === user?.id,
+ delete: user?.role === 'admin',
+ },
+ },
+ event
+ )
+})
+```
+
+
+ Because `setup()` is scoped to the current request's `event.context`, calling
+ it again from a nested route handler in the **same** request simply replaces
+ the rules for that request. Other requests are unaffected.
+
+
+If you omit `event`, Permix tries h3's current-request helpers when they exist (`getRequestEvent` / `useEvent`). In Nitro handlers and Vue server components, pass `event` or `useRequestEvent()` so this works on all Nuxt versions.
+
+
+
+
+
+## Check on the server
+
+Anywhere a server route or Vue server component runs in that request, you can use `check()`. Pass `event` (or `useRequestEvent()`) when you are not relying on AsyncLocalStorage:
+
+```ts title="server/api/posts/index.post.ts"
+import { permix } from '~/lib/permix'
+
+export default defineEventHandler((event) => {
+ if (!permix.get(event).check('post.create')) {
+ throw createError({ statusCode: 403, statusMessage: 'Forbidden' })
+ }
+
+ // create the post...
+ return { ok: true }
+})
+```
+
+```vue title="pages/posts/[id].vue"
+
+```
+
+You can also reach the underlying core instance through `permix.get(event)` if you need methods like `isReady()` or `getRules()`.
+
+
+
+
+
+## Send permissions to the client
+
+Use `dehydrate()` to serialize the request's permissions and hand them to a client provider. The server cannot send the Permix instance itself across the boundary — only the JSON state.
+
+`permix/nuxt` is **server-only**. Create a client singleton with core `permix` and wrap the app with `PermixProvider` + `PermixHydrate` from `permix/vue`. Dehydrate in a server plugin so the client bundle never imports `h3`:
+
+```ts title="lib/permix-client.ts"
+import { createPermix } from 'permix'
+
+// One singleton per browser tab. The same type definition as on the server.
+export const permix = createPermix<{
+ post: [
+ { name: 'create'; type: { id: string; authorId: string } },
+ { name: 'read'; type: { id: string; authorId: string } },
+ { name: 'update'; type: { id: string; authorId: string } },
+ { name: 'delete'; type: { id: string; authorId: string } },
+ ]
+}>()
+```
+
+```ts title="plugins/permix.server.ts"
+import { permix } from '~/lib/permix'
+
+export default defineNuxtPlugin(() => {
+ const event = useRequestEvent()
+ useState('permix-state', () => (event ? permix.dehydrate(event) : null))
+})
+```
+
+```vue title="app.vue"
+
+
+
+
+
+
+
+
+
+```
+
+
+ `hydrate()` restores the boolean state but does not flip `isReady` on its own
+ — function-based rules are lost during serialization. If you need `isReady` on
+ the client (e.g. to gate UI on `usePermix(...).isReady`), call
+ `permix.setup(...)` on the client too with the same shape (using booleans and
+ any function rules you want active client-side). See the [Hydration
+ guide](/docs/guide/hydration) for details.
+
+
+
+
+
+
+## Use on the client
+
+From any Vue component, import the client singleton and `usePermix` from `permix/vue`:
+
+```vue title="components/EditButton.vue"
+
+
+
+ Edit post
+
+```
+
+If you prefer the component API, create checkers with `createComponents` from `permix/vue` — see the [Vue integration](/docs/integrations/vue#components) for details.
+
+
+
+
+
+## Templates
+
+`createPermix` exposes the same `template()` helper as the core API for reusing rule sets:
+
+```ts title="lib/permix.ts"
+import { createPermix } from 'permix/nuxt'
+
+export const permix = createPermix<{
+ post: ['create', 'read', 'update', 'delete']
+}>()
+
+export const adminTemplate = permix.template({
+ post: { create: true, read: true, update: true, delete: true },
+})
+
+export const guestTemplate = permix.template({
+ post: { create: false, read: true, update: false, delete: false },
+})
+```
+
+```ts title="server/middleware/permix.ts"
+import { permix, adminTemplate, guestTemplate } from '~/lib/permix'
+
+export default defineEventHandler((event) => {
+ const user = event.context.user
+ permix.setup(
+ user?.role === 'admin' ? adminTemplate() : guestTemplate(),
+ event
+ )
+})
+```
+
+
+
+
+
+## Example
+
+You can find a runnable example of the Nuxt integration [here](https://github.com/letstri/permix/tree/main/examples/nuxt).
+
+
+
+
+
+## How per-request isolation works
+
+`createPermix` from `permix/nuxt` stores one core instance per factory on the current Nitro event's `context`. Inside one request:
+
+- The first call to `setup`/`check`/`get`/`dehydrate` creates (or reuses) **one** instance.
+- All subsequent calls in the same request — across middleware, API routes, and Vue server components that share that event — share that instance.
+
+Across concurrent requests, each event object gets its **own** instance. State never leaks between users.
+
+
+ Do **not** store the result of `permix.get()` (or any rule data) in
+ module-level variables. That would defeat per-request isolation. Always go
+ through `permix.check()` / `permix.get(event)` so the request-scoped store is
+ consulted.
+
+
+## API
+
+### `createPermix()`
+
+Returns an object with the following methods:
+
+| Method | Description |
+| --- | --- |
+| `setup(rules, event?)` | Set the per-request permission rules. Pass `event` in Nitro handlers (or `useRequestEvent()` in Vue). |
+| `check(...args)` | Check a permission against the current request's rules (via `getRequestEvent()`). Same signature as the core `check`. |
+| `get(event?)` | Return the underlying [`Permix`](/docs/guide/instance) instance for the request. |
+| `getRules(event?)` | Return the current rules object for the request-scoped instance, or `null`. |
+| `dehydrate(event?)` | Serialize the current request's rules to JSON (for `` on the client). |
+| `template(rules)` | Create a reusable rule set. Same as the core [`template`](/docs/guide/template). |
diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json
index dbd79bfc..cb8716a4 100644
--- a/docs/content/docs/meta.json
+++ b/docs/content/docs/meta.json
@@ -18,6 +18,7 @@
"integrations/react",
"integrations/next",
"integrations/tanstack-start",
+ "integrations/nuxt",
"integrations/vue",
"integrations/solid",
"integrations/svelte",
diff --git a/docs/content/docs/migration-v3-to-v4.mdx b/docs/content/docs/migration-v3-to-v4.mdx
index 20be84ca..effc20d4 100644
--- a/docs/content/docs/migration-v3-to-v4.mdx
+++ b/docs/content/docs/migration-v3-to-v4.mdx
@@ -340,6 +340,7 @@ These are additive — migrate the core API first, then adopt what you need:
| ----------------------- | ------------------------------------------- |
| `permix/next` | Next.js App Router, request-scoped instance |
| `permix/tanstack-start` | TanStack Start middleware and SSR |
+| `permix/nuxt` | Nuxt / Nitro, request-scoped instance |
| `permix/server` | Framework-agnostic fetch middleware |
| `permix/svelte` | Svelte 5 runes |
| `permix/drizzle` | Rules from Drizzle v1 schema |
diff --git a/docs/content/docs/quick-start.mdx b/docs/content/docs/quick-start.mdx
index c6bc2196..83290357 100644
--- a/docs/content/docs/quick-start.mdx
+++ b/docs/content/docs/quick-start.mdx
@@ -170,6 +170,10 @@ Continuing from the quick start, you can now explore how Permix integrates with
Integration with TanStack Start.
+
+ Integration with Nuxt via Nitro request isolation.
+
+
Integration with Solid via provider and hook.
diff --git a/examples/nuxt/main.ts b/examples/nuxt/main.ts
new file mode 100644
index 00000000..6d1a3321
--- /dev/null
+++ b/examples/nuxt/main.ts
@@ -0,0 +1,66 @@
+import { createServer } from 'node:http'
+
+import {
+ createApp,
+ createRouter,
+ eventHandler,
+ setResponseStatus,
+ toNodeListener,
+} from 'h3'
+import type { ValidateDefinition } from 'permix'
+import { createPermix } from 'permix/nuxt'
+
+type PermissionsDefinition = ValidateDefinition<{
+ user: ['read', 'write']
+}>
+
+const permix = createPermix()
+
+const app = createApp()
+const router = createRouter()
+
+function setup(event: Parameters[1]) {
+ permix.setup(
+ {
+ user: {
+ read: true,
+ write: false,
+ },
+ },
+ event
+ )
+}
+
+router.get(
+ '/',
+ eventHandler((event) => {
+ setup(event)
+ return { canRead: permix.get(event).check('user.read') }
+ })
+)
+
+router.get(
+ '/write',
+ eventHandler((event) => {
+ setup(event)
+ if (!permix.get(event).check('user.write')) {
+ setResponseStatus(event, 403)
+ return { error: 'Forbidden' }
+ }
+ return { ok: true }
+ })
+)
+
+router.get(
+ '/state',
+ eventHandler((event) => {
+ setup(event)
+ return permix.dehydrate(event)
+ })
+)
+
+app.use(router)
+
+createServer(toNodeListener(app)).listen(3000, () => {
+ console.log('Server is running on port 3000')
+})
diff --git a/examples/nuxt/package.json b/examples/nuxt/package.json
new file mode 100644
index 00000000..d655f2bc
--- /dev/null
+++ b/examples/nuxt/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "nuxt",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "check-types": "tsc --noEmit",
+ "start": "tsx main.ts"
+ },
+ "dependencies": {
+ "h3": "^1.15.4",
+ "permix": "workspace:*"
+ },
+ "devDependencies": {
+ "@types/node": "^25.9.1",
+ "tsx": "^4.22.4"
+ }
+}
diff --git a/examples/nuxt/tsconfig.json b/examples/nuxt/tsconfig.json
new file mode 100644
index 00000000..48ce3c61
--- /dev/null
+++ b/examples/nuxt/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "strict": true,
+ "types": ["node"],
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ }
+}
diff --git a/permix/package.json b/permix/package.json
index dfbefd33..2535f946 100644
--- a/permix/package.json
+++ b/permix/package.json
@@ -11,6 +11,7 @@
"frontend",
"javascript",
"nextjs",
+ "nuxt",
"permissions",
"permissions-management",
"rbac",
@@ -113,6 +114,10 @@
"types": "./dist/next/index.d.mts",
"import": "./dist/next/index.mjs"
},
+ "./nuxt": {
+ "types": "./dist/nuxt/index.d.mts",
+ "import": "./dist/nuxt/index.mjs"
+ },
"./tanstack-start": {
"types": "./dist/tanstack-start/index.d.mts",
"import": "./dist/tanstack-start/index.mjs"
@@ -146,6 +151,7 @@
"@vue/test-utils": "^2.4.10",
"drizzle-orm": "1.0.0-rc.3",
"effect": "^3.21.2",
+ "h3": "^1.15.11",
"happy-dom": "^20.9.0",
"react-dom": "^19.2.6",
"supertest": "^7.2.2",
@@ -168,6 +174,7 @@
"express": ">=4",
"fastify": ">=5",
"fastify-plugin": ">=5",
+ "h3": ">=1.13",
"hono": ">=4",
"next": ">=14",
"react": ">=18",
@@ -204,6 +211,9 @@
"fastify-plugin": {
"optional": true
},
+ "h3": {
+ "optional": true
+ },
"hono": {
"optional": true
},
diff --git a/permix/skills/permix/SKILL.md b/permix/skills/permix/SKILL.md
index b3ebc1b5..035aba3f 100644
--- a/permix/skills/permix/SKILL.md
+++ b/permix/skills/permix/SKILL.md
@@ -1,7 +1,7 @@
---
name: permix
description: >-
- Applies Permix authorization once a schema exists: permix.check() paths and ReBAC callbacks, frontend bindings (permix/react, permix/vue, permix/solid, permix/svelte) with SSR dehydrate/hydrate for Next.js and TanStack Start, and server middleware (permix/express, hono, fastify, trpc, orpc, node, elysia). Use for anything past initial setup — checking permissions, gating UI, or protecting routes. For creating the schema and first `permix.setup()`, use permix-getting-started first.
+ Applies Permix authorization once a schema exists: permix.check() paths and ReBAC callbacks, frontend bindings (permix/react, permix/vue, permix/solid, permix/svelte) with SSR dehydrate/hydrate for Next.js, TanStack Start, and Nuxt, and server middleware (permix/express, hono, fastify, trpc, orpc, node, elysia). Use for anything past initial setup — checking permissions, gating UI, or protecting routes. For creating the schema and first `permix.setup()`, use permix-getting-started first.
metadata:
type: core
library: permix
@@ -19,6 +19,7 @@ sources:
- 'letstri/permix:docs/content/docs/integrations/svelte.mdx'
- 'letstri/permix:docs/content/docs/integrations/next.mdx'
- 'letstri/permix:docs/content/docs/integrations/tanstack-start.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/nuxt.mdx'
- 'letstri/permix:docs/content/docs/integrations/express.mdx'
- 'letstri/permix:docs/content/docs/integrations/hono.mdx'
- 'letstri/permix:docs/content/docs/integrations/fastify.mdx'
@@ -37,7 +38,7 @@ Assumes a `permix` instance already exists (see **permix-getting-started**). Loa
| Task | Reference |
| --- | --- |
| `permix.check()` paths, callbacks, `~all`/`~any`, ReBAC/ABAC with entity data, `isReady` | [references/check.md](references/check.md) |
-| React, Vue, Solid, or Svelte UI — `PermixProvider`, `usePermix`, `createComponents`, SSR `dehydrate`/`hydrate` for Next.js / TanStack Start | [references/frontend.md](references/frontend.md) |
+| React, Vue, Solid, or Svelte UI — `PermixProvider`, `usePermix`, `createComponents`, SSR `dehydrate`/`hydrate` for Next.js / TanStack Start / Nuxt | [references/frontend.md](references/frontend.md) |
| Protecting Express, Hono, Fastify, tRPC, oRPC, Node, or Elysia routes — `setupMiddleware`, `checkMiddleware` | [references/server.md](references/server.md) |
## Rules that apply everywhere
diff --git a/permix/skills/permix/references/frontend.md b/permix/skills/permix/references/frontend.md
index 510f619c..55b99d89 100644
--- a/permix/skills/permix/references/frontend.md
+++ b/permix/skills/permix/references/frontend.md
@@ -188,9 +188,9 @@ function App({
Run client `permix.setup(...)` where you restore the session (e.g. after `PermixHydrate` mounts or in the same auth effect).
-### Next.js / TanStack Start
+### Next.js / TanStack Start / Nuxt
-Use framework helpers from `permix/next` or `permix/tanstack-start` when available — they wire dehydrate/hydrate into the framework data flow.
+Use framework helpers from `permix/next`, `permix/tanstack-start`, or `permix/nuxt` when available — they wire dehydrate/hydrate into the framework data flow. Nuxt hydrates the client with `PermixProvider` / `PermixHydrate` from `permix/vue`.
In TanStack Start, `permix.get(context)` only works in server functions and server routes. To check inside `beforeLoad`/`loader`, put a core instance on the **router context** in `getRouter()` (`context: { permix }`), type it with `createRootRouteWithContext`, hydrate it in the root route's `beforeLoad`, then call `context.permix.check(...)` in any child route. Passing only the context type without the runtime value leaves `context.permix` undefined.
@@ -200,6 +200,7 @@ Docs:
- https://permix.letstri.dev/docs/integrations/next
- https://permix.letstri.dev/docs/integrations/tanstack-start
+- https://permix.letstri.dev/docs/integrations/nuxt
### Flow diagram
@@ -225,5 +226,6 @@ For static-only permissions (all booleans), dehydrate + hydrate + `setup` with t
- Solid: https://github.com/letstri/permix/tree/main/examples/solid
- Svelte: https://github.com/letstri/permix/tree/main/examples/svelte
- Next.js (SSR): https://github.com/letstri/permix/tree/main/examples/next
+- Nuxt (SSR): https://github.com/letstri/permix/tree/main/examples/nuxt
- Role templates: https://github.com/letstri/permix/tree/main/examples/role-based
- ReBAC: https://github.com/letstri/permix/tree/main/examples/rebac
diff --git a/permix/src/nuxt/index.ts b/permix/src/nuxt/index.ts
new file mode 100644
index 00000000..60dafabb
--- /dev/null
+++ b/permix/src/nuxt/index.ts
@@ -0,0 +1 @@
+export * from './permix'
diff --git a/permix/src/nuxt/permix.test.ts b/permix/src/nuxt/permix.test.ts
new file mode 100644
index 00000000..7264b319
--- /dev/null
+++ b/permix/src/nuxt/permix.test.ts
@@ -0,0 +1,215 @@
+import { describe, expect, it, vi } from 'vitest'
+
+import { createPermix } from './permix'
+import type { NuxtEvent } from './permix'
+
+let currentEvent: NuxtEvent | undefined
+
+vi.mock('h3', () => ({
+ getRequestEvent: () => currentEvent,
+}))
+
+async function withEvent(
+ fn: (event: NuxtEvent) => T | Promise
+): Promise {
+ currentEvent = { context: {} }
+ try {
+ return await fn(currentEvent)
+ } finally {
+ currentEvent = undefined
+ }
+}
+
+describe('nuxt createPermix', () => {
+ it('throws when no request event is available', () => {
+ const permix = createPermix<{
+ post: ['create']
+ }>()
+
+ expect(() => {
+ permix.setup({
+ post: {
+ create: true,
+ },
+ })
+ }).toThrow(/No request event found/)
+ })
+
+ it('sets up rules and checks permissions', async () => {
+ await withEvent(() => {
+ const permix = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ permix.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ expect(permix.check('post.create')).toBe(true)
+ expect(permix.check('post.read')).toBe(false)
+ })
+ })
+
+ it('works with data resolved before setup', async () => {
+ await withEvent(async () => {
+ const permix = createPermix<{
+ post: ['create']
+ }>()
+
+ const user = await Promise.resolve({ role: 'admin' as const })
+
+ permix.setup({
+ post: {
+ create: user.role === 'admin',
+ },
+ })
+
+ expect(permix.check('post.create')).toBe(true)
+ })
+ })
+
+ it('exposes the underlying core instance via get()', async () => {
+ await withEvent((event) => {
+ const permix = createPermix<{
+ post: ['create']
+ }>()
+
+ permix.setup({ post: { create: true } }, event)
+
+ const core = permix.get(event)
+
+ expect(core.isReady()).toBe(true)
+ expect(core.check('post.create')).toBe(true)
+ })
+ })
+
+ it('reads the current rules with getRules', async () => {
+ await withEvent(() => {
+ const permix = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ expect(permix.getRules()).toBeNull()
+
+ permix.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ expect(permix.getRules()).toStrictEqual({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+ })
+ })
+
+ it('dehydrates the request-scoped state', async () => {
+ await withEvent(() => {
+ const permix = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ permix.setup({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+
+ expect(permix.dehydrate()).toStrictEqual({
+ post: {
+ create: true,
+ read: false,
+ },
+ })
+ })
+ })
+
+ it('reuses the same instance across calls in the same request scope', async () => {
+ await withEvent(() => {
+ const permix = createPermix<{
+ post: ['create']
+ }>()
+
+ permix.setup({ post: { create: true } })
+
+ expect(permix.get()).toBe(permix.get())
+ expect(permix.check('post.create')).toBe(true)
+ })
+ })
+
+ it('isolates state between independent factories', async () => {
+ await withEvent(() => {
+ const permixA = createPermix<{ post: ['create'] }>()
+ const permixB = createPermix<{ post: ['create'] }>()
+
+ permixA.setup({ post: { create: true } })
+ permixB.setup({ post: { create: false } })
+
+ expect(permixA.check('post.create')).toBe(true)
+ expect(permixB.check('post.create')).toBe(false)
+ expect(permixA.get()).not.toBe(permixB.get())
+ })
+ })
+
+ it('isolates state between concurrent events', () => {
+ const permix = createPermix<{ post: ['create'] }>()
+ const eventA: NuxtEvent = { context: {} }
+ const eventB: NuxtEvent = { context: {} }
+
+ permix.setup({ post: { create: true } }, eventA)
+ permix.setup({ post: { create: false } }, eventB)
+
+ expect(permix.get(eventA).check('post.create')).toBe(true)
+ expect(permix.get(eventB).check('post.create')).toBe(false)
+ expect(permix.get(eventA)).not.toBe(permix.get(eventB))
+ })
+
+ it('creates reusable templates', () => {
+ const permix = createPermix<{
+ post: ['create', 'read']
+ }>()
+
+ const adminTemplate = permix.template({
+ post: {
+ create: true,
+ read: true,
+ },
+ })
+
+ expect(adminTemplate()).toStrictEqual({
+ post: {
+ create: true,
+ read: true,
+ },
+ })
+ })
+
+ it('supports parameterized templates', () => {
+ const permix = createPermix<{
+ post: [{ name: 'edit'; type: { authorId: string } }]
+ }>()
+
+ const template = permix.template((userId: string) => ({
+ post: {
+ edit: (post: { authorId: string } | undefined) =>
+ post?.authorId === userId,
+ },
+ }))
+
+ const rules = template('user-1')
+ const editFn = rules.post.edit as (
+ post: { authorId: string } | undefined
+ ) => boolean
+
+ expect(editFn({ authorId: 'user-1' })).toBe(true)
+ expect(editFn({ authorId: 'user-2' })).toBe(false)
+ })
+})
diff --git a/permix/src/nuxt/permix.ts b/permix/src/nuxt/permix.ts
new file mode 100644
index 00000000..b0e242f7
--- /dev/null
+++ b/permix/src/nuxt/permix.ts
@@ -0,0 +1,161 @@
+import * as h3 from 'h3'
+
+import type { Permix as PermixCore } from '../core'
+import { createPermix as createPermixCore, createTemplate } from '../core'
+import type { Definition } from '../core/definitions'
+import type { PermixHooks, Rules, RulesPaths } from '../core/permix'
+import type { DehydratedState } from '../core/rules'
+
+/**
+ * Minimal Nitro/h3 event shape. Compatible with `H3Event` from `h3` and
+ * Nuxt's `useRequestEvent()` return value.
+ */
+export interface NuxtEvent {
+ context: object
+}
+
+interface H3RequestEventApi {
+ getRequestEvent?: () => NuxtEvent | undefined
+ useEvent?: () => NuxtEvent
+}
+
+function readCurrentEvent(): NuxtEvent | undefined {
+ // h3 1.x has no ALS helper; Nitro/h3 v2 may expose getRequestEvent or useEvent.
+ const runtime = h3 as H3RequestEventApi
+ if (typeof runtime.getRequestEvent === 'function') {
+ return runtime.getRequestEvent()
+ }
+ if (typeof runtime.useEvent === 'function') {
+ try {
+ return runtime.useEvent()
+ } catch {
+ return undefined
+ }
+ }
+ return undefined
+}
+
+function resolveEvent(
+ event: NuxtEvent | undefined,
+ key: string | symbol
+): NuxtEvent {
+ if (event) {
+ return event
+ }
+ const current = readCurrentEvent()
+ if (current) {
+ return current
+ }
+ throw new Error(
+ `[Permix]: No request event found for key ${String(key)}. Call setup() inside a Nuxt/Nitro request, or pass the event.`
+ )
+}
+
+function getOrCreate(
+ event: NuxtEvent,
+ key: string | symbol,
+ create: () => PermixCore
+): PermixCore {
+ const context = event.context as Record
+ const existing = context[key] as PermixCore | undefined
+ if (existing) {
+ return existing
+ }
+ const instance = create()
+ context[key] = instance
+ return instance
+}
+
+/**
+ * Create a per-request Permix instance for Nuxt / Nitro.
+ *
+ * The instance is stored on the current request's `event.context`, so server
+ * routes, server middleware, and Vue server components in the same request
+ * share one instance while concurrent requests stay isolated.
+ *
+ * @example
+ * ```ts
+ * // lib/permix.ts
+ * import { createPermix } from 'permix/nuxt'
+ *
+ * export const permix = createPermix<{
+ * post: ['create', 'read', 'update', 'delete']
+ * }>()
+ * ```
+ *
+ * ```ts
+ * // server/middleware/permix.ts
+ * import { permix } from '~/lib/permix'
+ *
+ * export default defineEventHandler((event) => {
+ * permix.setup({
+ * post: { create: true, read: true, update: false, delete: false },
+ * }, event)
+ * })
+ * ```
+ *
+ * @link https://permix.letstri.dev/docs/integrations/nuxt
+ */
+export function createPermix() {
+ const key: symbol = Symbol('permix')
+
+ function getPermix(event?: NuxtEvent): PermixCore {
+ const resolved = resolveEvent(event, key)
+ return getOrCreate(resolved, key, () => createPermixCore())
+ }
+
+ function setup(rules: Rules, event?: NuxtEvent): void {
+ getPermix(event).setup(rules)
+ }
+
+ const check: PermixCore['check'] = (...args) => getPermix().check(...args)
+
+ function dehydrate(event?: NuxtEvent): DehydratedState {
+ return getPermix(event).dehydrate()
+ }
+
+ function get(event?: NuxtEvent): PermixCore {
+ return getPermix(event)
+ }
+
+ function getRules(event?: NuxtEvent): Rules | null {
+ return getPermix(event).getRules()
+ }
+
+ function template(rules: Rules | ((param: T) => Rules)) {
+ return createTemplate(rules)
+ }
+
+ function hook>(
+ name: K,
+ fn: PermixHooks[K],
+ event?: NuxtEvent
+ ) {
+ return getPermix(event).hook(name, fn)
+ }
+
+ function hookOnce>(
+ name: K,
+ fn: PermixHooks[K],
+ event?: NuxtEvent
+ ) {
+ getPermix(event).hookOnce(name, fn)
+ }
+
+ return {
+ setup,
+ check,
+ dehydrate,
+ get,
+ getRules,
+ template,
+ hook,
+ hookOnce,
+ $inferDefinition: undefined as unknown as D,
+ $inferPath: undefined as unknown as RulesPaths,
+ }
+}
+
+export type NuxtPermix = ReturnType<
+ typeof createPermix
+>
diff --git a/permix/tsdown.config.ts b/permix/tsdown.config.ts
index 00e9a0a3..8fa2de2c 100644
--- a/permix/tsdown.config.ts
+++ b/permix/tsdown.config.ts
@@ -21,6 +21,7 @@ export default defineConfig({
'./src/drizzle/index.ts',
'./src/drizzle/legacy/index.ts',
'./src/next/index.ts',
+ './src/nuxt/index.ts',
'./src/tanstack-start/index.ts',
],
dts: {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 761d130a..d410adbe 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -297,6 +297,22 @@ importers:
specifier: ^6.0.3
version: 6.0.3
+ examples/nuxt:
+ dependencies:
+ h3:
+ specifier: ^1.15.4
+ version: 1.15.11
+ permix:
+ specifier: workspace:*
+ version: link:../../permix
+ devDependencies:
+ '@types/node':
+ specifier: ^25.9.1
+ version: 25.9.1
+ tsx:
+ specifier: ^4.22.4
+ version: 4.22.4
+
examples/react:
dependencies:
permix:
@@ -435,7 +451,7 @@ importers:
version: 1.167.1(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-router-ssr-query':
specifier: latest
- version: 1.167.1(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ version: 1.167.2(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start':
specifier: latest
version: 1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -548,7 +564,7 @@ importers:
version: 4.12.23
next:
specifier: '>=14'
- version: 16.2.6(@babel/core@7.29.7)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ version: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react:
specifier: '>=18'
version: 19.2.6
@@ -607,6 +623,9 @@ importers:
effect:
specifier: ^3.21.2
version: 3.21.2
+ h3:
+ specifier: ^1.15.11
+ version: 1.15.11
happy-dom:
specifier: ^20.9.0
version: 20.9.0
@@ -2634,12 +2653,12 @@ packages:
'@tanstack/router-core':
optional: true
- '@tanstack/react-router-ssr-query@1.167.1':
- resolution: {integrity: sha512-W9j5JPnBikyafvuUfykFfHIWod58OAbAAa5leNkXBcoDoocghMmu6w9uZOmUZvAWT7CSvgj5tBUtF7CM2OoHXQ==}
+ '@tanstack/react-router-ssr-query@1.167.2':
+ resolution: {integrity: sha512-yRvy0VJ00R8huPuquk+qyYQGMpYRc/G33mc6/yZ4f7LaBZz9h/VbACjmLzhm/+6u5WSmUt6ouJJZnI1/5Npfag==}
engines: {node: '>=20.19'}
peerDependencies:
- '@tanstack/query-core': '>=5.90.0'
- '@tanstack/react-query': '>=5.90.0'
+ '@tanstack/query-core': '>=5.102.0'
+ '@tanstack/react-query': '>=5.102.0'
'@tanstack/react-router': '>=1.127.0'
react: '>=18.0.0 || >=19.0.0'
react-dom: '>=18.0.0 || >=19.0.0'
@@ -2927,11 +2946,11 @@ packages:
webpack:
optional: true
- '@tanstack/router-ssr-query-core@1.169.1':
- resolution: {integrity: sha512-rngux8s/3mPQzcjLYDLkNU31coYVyCgrVTfpdwqUdY5jIEHqGTXrO73DTkPR1PppwYUeVhmNCgl8TctRcnupjg==}
+ '@tanstack/router-ssr-query-core@1.169.2':
+ resolution: {integrity: sha512-7pO65Aiq/1+aS3Mb6vSGtIjzQ/YGv9JTfBbn2EYlHcZnJ4s4ZGCeTPI847geB1RfJqVhfwxM8bZgGM51mYkolg==}
engines: {node: '>=20.19'}
peerDependencies:
- '@tanstack/query-core': '>=5.90.0'
+ '@tanstack/query-core': '>=5.102.0'
'@tanstack/router-core': '>=1.127.0'
'@tanstack/router-utils@1.162.1':
@@ -3811,6 +3830,9 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+ cookie-es@1.2.3:
+ resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==}
+
cookie-es@3.1.1:
resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==}
@@ -3847,6 +3869,9 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
+ crossws@0.3.5:
+ resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==}
+
crossws@0.4.5:
resolution: {integrity: sha512-wUR89x/Rw7/8t+vn0CmGDYM9TD6VtARGb0LD5jq2wjtMy1vCP4M+sm6N6TigWeTYvnA8MoW29NqqXD0ep0rfBA==}
peerDependencies:
@@ -4760,6 +4785,9 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+ h3@1.15.11:
+ resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==}
+
h3@2.0.1-rc.20:
resolution: {integrity: sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg==}
engines: {node: '>=20.11.1'}
@@ -4920,6 +4948,9 @@ packages:
resolution: {integrity: sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==}
engines: {node: '>= 10'}
+ iron-webcrypto@1.2.1:
+ resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
+
is-alphabetical@2.0.1:
resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
@@ -5557,6 +5588,9 @@ packages:
node-fetch-native@1.6.7:
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
+ node-mock-http@1.0.5:
+ resolution: {integrity: sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==}
+
node-releases@2.0.46:
resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==}
engines: {node: '>=18'}
@@ -5846,6 +5880,9 @@ packages:
resolution: {integrity: sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA==}
engines: {node: '>=14.18.0'}
+ radix3@1.1.2:
+ resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
+
range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
@@ -6507,6 +6544,9 @@ packages:
unconfig@7.5.0:
resolution: {integrity: sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==}
+ uncrypto@0.1.3:
+ resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
+
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
@@ -8665,12 +8705,12 @@ snapshots:
transitivePeerDependencies:
- csstype
- '@tanstack/react-router-ssr-query@1.167.1(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
+ '@tanstack/react-router-ssr-query@1.167.2(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
'@tanstack/query-core': 5.101.0
'@tanstack/react-query': 5.101.0(react@19.2.6)
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/router-ssr-query-core': 1.169.1(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)
+ '@tanstack/router-ssr-query-core': 1.169.2(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
transitivePeerDependencies:
@@ -9062,7 +9102,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tanstack/router-ssr-query-core@1.169.1(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)':
+ '@tanstack/router-ssr-query-core@1.169.2(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)':
dependencies:
'@tanstack/query-core': 5.101.0
'@tanstack/router-core': 1.171.27
@@ -9673,7 +9713,7 @@ snapshots:
obug: 2.1.1
std-env: 4.1.0
tinyrainbow: 3.1.0
- vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitest: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vitest/expect@4.1.8':
dependencies:
@@ -10080,6 +10120,8 @@ snapshots:
convert-source-map@2.0.0: {}
+ cookie-es@1.2.3: {}
+
cookie-es@3.1.1: {}
cookie-signature@1.2.2: {}
@@ -10111,6 +10153,10 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
+ crossws@0.3.5:
+ dependencies:
+ uncrypto: 0.1.3
+
crossws@0.4.5(srvx@0.11.16):
optionalDependencies:
srvx: 0.11.16
@@ -10964,6 +11010,18 @@ snapshots:
graceful-fs@4.2.11: {}
+ h3@1.15.11:
+ dependencies:
+ cookie-es: 1.2.3
+ crossws: 0.3.5
+ defu: 6.1.7
+ destr: 2.0.5
+ iron-webcrypto: 1.2.1
+ node-mock-http: 1.0.5
+ radix3: 1.1.2
+ ufo: 1.6.4
+ uncrypto: 0.1.3
+
h3@2.0.1-rc.20(crossws@0.4.5(srvx@0.11.16)):
dependencies:
rou3: 0.8.1
@@ -11186,6 +11244,8 @@ snapshots:
ipaddr.js@2.4.0: {}
+ iron-webcrypto@1.2.1: {}
+
is-alphabetical@2.0.1: {}
is-alphanumerical@2.0.1:
@@ -11982,30 +12042,6 @@ snapshots:
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- next@16.2.6(@babel/core@7.29.7)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
- dependencies:
- '@next/env': 16.2.6
- '@swc/helpers': 0.5.15
- baseline-browser-mapping: 2.10.33
- caniuse-lite: 1.0.30001793
- postcss: 8.4.31
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.6)
- optionalDependencies:
- '@next/swc-darwin-arm64': 16.2.6
- '@next/swc-darwin-x64': 16.2.6
- '@next/swc-linux-arm64-gnu': 16.2.6
- '@next/swc-linux-arm64-musl': 16.2.6
- '@next/swc-linux-x64-gnu': 16.2.6
- '@next/swc-linux-x64-musl': 16.2.6
- '@next/swc-win32-arm64-msvc': 16.2.6
- '@next/swc-win32-x64-msvc': 16.2.6
- sharp: 0.34.5
- transitivePeerDependencies:
- - '@babel/core'
- - babel-plugin-macros
-
next@16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies:
'@next/env': 16.2.6
@@ -12039,7 +12075,7 @@ snapshots:
postcss: 8.4.31
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.6)
+ styled-jsx: 5.1.6(react@19.2.6)
optionalDependencies:
'@next/swc-darwin-arm64': 16.2.6
'@next/swc-darwin-x64': 16.2.6
@@ -12053,7 +12089,6 @@ snapshots:
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
- optional: true
nf3@0.3.17: {}
@@ -12109,6 +12144,8 @@ snapshots:
node-fetch-native@1.6.7: {}
+ node-mock-http@1.0.5: {}
+
node-releases@2.0.46: {}
nopt@7.2.1:
@@ -12438,6 +12475,8 @@ snapshots:
radash@12.1.1: {}
+ radix3@1.1.2: {}
+
range-parser@1.2.1: {}
raw-body@3.0.2:
@@ -12946,17 +12985,15 @@ snapshots:
dependencies:
inline-style-parser: 0.2.7
- styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.6):
+ styled-jsx@5.1.6(react@19.2.4):
dependencies:
client-only: 0.0.1
- react: 19.2.6
- optionalDependencies:
- '@babel/core': 7.29.7
+ react: 19.2.4
- styled-jsx@5.1.6(react@19.2.4):
+ styled-jsx@5.1.6(react@19.2.6):
dependencies:
client-only: 0.0.1
- react: 19.2.4
+ react: 19.2.6
stylis@4.4.0: {}
@@ -13216,6 +13253,8 @@ snapshots:
quansync: 1.0.0
unconfig-core: 7.5.0
+ uncrypto@0.1.3: {}
+
undici-types@6.21.0: {}
undici-types@7.16.0: {}
From 48dd91edd02094e6ffb3ab7917482630dceed3c2 Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 15:04:38 +0200
Subject: [PATCH 13/28] feat: add Astro adapter with locals-based middleware
Store a per-request Permix instance on Astro locals so middleware, endpoints, and pages can share rules without tying UI islands to a new client adapter.
Co-authored-by: Cursor
---
_artifacts/domain_map.yaml | 7 +-
_artifacts/skill_tree.yaml | 5 +-
docs/content/docs/comparison.mdx | 3 +-
docs/content/docs/integrations/astro.mdx | 233 +++++++++++++
docs/content/docs/meta.json | 1 +
docs/content/docs/migration-v3-to-v4.mdx | 1 +
docs/content/docs/quick-start.mdx | 4 +
examples/astro/main.ts | 54 +++
examples/astro/package.json | 16 +
examples/astro/tsconfig.json | 11 +
permix/package.json | 5 +
permix/skills/permix/SKILL.md | 5 +-
permix/skills/permix/references/server.md | 1 +
permix/src/astro/index.ts | 1 +
permix/src/astro/permix.test.ts | 399 ++++++++++++++++++++++
permix/src/astro/permix.ts | 212 ++++++++++++
permix/tsdown.config.ts | 1 +
pnpm-lock.yaml | 76 ++---
18 files changed, 984 insertions(+), 51 deletions(-)
create mode 100644 docs/content/docs/integrations/astro.mdx
create mode 100644 examples/astro/main.ts
create mode 100644 examples/astro/package.json
create mode 100644 examples/astro/tsconfig.json
create mode 100644 permix/src/astro/index.ts
create mode 100644 permix/src/astro/permix.test.ts
create mode 100644 permix/src/astro/permix.ts
diff --git a/_artifacts/domain_map.yaml b/_artifacts/domain_map.yaml
index 318aa7c9..d03694c1 100644
--- a/_artifacts/domain_map.yaml
+++ b/_artifacts/domain_map.yaml
@@ -34,7 +34,7 @@ domains:
slug: server
description: >
Per-request setupMiddleware and checkMiddleware for Express, Hono, Fastify,
- tRPC, oRPC, Node, and Elysia.
+ tRPC, oRPC, Node, Elysia, and Astro.
- name: 'SSR and hydration'
slug: ssr
@@ -78,7 +78,7 @@ skills:
~all/~any, entity-aware ReBAC rules, isReady/isReadyAsync;
PermixProvider/usePermix/createComponents and SSR dehydrate/hydrate for
React, Vue, Solid, Svelte, Next.js, TanStack Start; setupMiddleware and
- checkMiddleware for Express, Hono, Fastify, tRPC, oRPC, Node, Elysia.
+ checkMiddleware for Express, Hono, Fastify, tRPC, oRPC, Node, Elysia, Astro.
Single skill with a thin SKILL.md router and three reference files
loaded on demand.
type: core
@@ -98,6 +98,7 @@ skills:
- orpc
- node
- elysia
+ - astro
covers:
- check
- isReady
@@ -144,6 +145,7 @@ skills:
- 'letstri/permix:docs/content/docs/integrations/orpc.mdx'
- 'letstri/permix:docs/content/docs/integrations/node.mdx'
- 'letstri/permix:docs/content/docs/integrations/server.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/astro.mdx'
- 'letstri/permix:docs/content/docs/integrations/elysia.mdx'
- 'letstri/permix:permix/src/core/check.ts'
@@ -153,6 +155,7 @@ coverage:
- examples/*
- next
- tanstack-start
+ - astro
failure_modes:
- mistake: 'Using v3 { action, dataType } schema shape'
diff --git a/_artifacts/skill_tree.yaml b/_artifacts/skill_tree.yaml
index 70b42866..e9b87b2d 100644
--- a/_artifacts/skill_tree.yaml
+++ b/_artifacts/skill_tree.yaml
@@ -40,7 +40,7 @@ skills:
isReady/isReadyAsync; PermixProvider/usePermix/createComponents and SSR
dehydrate/hydrate for React, Vue, Solid, Svelte, Next.js, TanStack
Start; setupMiddleware/checkMiddleware for Express, Hono, Fastify,
- tRPC, oRPC, Node, Elysia. Thin router with references loaded on demand.
+ tRPC, oRPC, Node, Elysia, Astro. Thin router with references loaded on demand.
requires:
- permix-getting-started
subsystems:
@@ -57,6 +57,7 @@ skills:
- orpc
- node
- elysia
+ - astro
references:
- 'references/check.md'
- 'references/frontend.md'
@@ -79,6 +80,7 @@ skills:
- 'letstri/permix:docs/content/docs/integrations/orpc.mdx'
- 'letstri/permix:docs/content/docs/integrations/node.mdx'
- 'letstri/permix:docs/content/docs/integrations/server.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/astro.mdx'
- 'letstri/permix:docs/content/docs/integrations/elysia.mdx'
- 'letstri/permix:permix/src/core/check.ts'
@@ -88,3 +90,4 @@ coverage:
- examples/*
- next
- tanstack-start
+ - astro
diff --git a/docs/content/docs/comparison.mdx b/docs/content/docs/comparison.mdx
index d0d9ea67..b7a69875 100644
--- a/docs/content/docs/comparison.mdx
+++ b/docs/content/docs/comparison.mdx
@@ -19,7 +19,7 @@ Permix is a library that provides a way to manage permissions in your applicatio
| Events | ✅ | ❌ |
| Simple DX | ✅ Create instance, use built-in integrations | ❌ In CASL you need to manage a lot of stuff manually (type-safe, hydration, etc.) |
| Modernity | ✅ Uses modern updates and features of each lib and framework | ❌ CASL was created a long time ago and hasn't updated the core |
-| Size | **2.64 kB** gzip (core) [react 0.86 kB, vue 0.87 kB, solid 0.82 kB, next 1.00 kB, tanstack-start 2.05 kB, svelte ~2.17 kB, …] | **6.17 kB** min+gzip (core) [@casl/react 0.62 kB] |
+| Size | **2.64 kB** gzip (core) [react 0.86 kB, vue 0.87 kB, solid 0.82 kB, next 1.00 kB, astro 1.19 kB, tanstack-start 2.05 kB, svelte ~2.17 kB, …] | **6.17 kB** min+gzip (core) [@casl/react 0.62 kB] |
Sizes are hard numbers from published builds — see [Bundle size](#bundle-size). Bracketed values are integration adapters imported on top of core.
@@ -42,6 +42,7 @@ Built with `pnpm run build` in the `permix` package (`tsdown` for all entries ex
| `permix/tanstack-start` | 2.05 kB | adapter |
| `permix/node` | 0.95 kB | adapter |
| `permix/server` | 1.10 kB | adapter |
+| `permix/astro` | 1.19 kB | adapter |
| `permix/express` | 0.91 kB | adapter |
| `permix/hono` | 0.88 kB | adapter |
| `permix/fastify` | 1.04 kB | adapter |
diff --git a/docs/content/docs/integrations/astro.mdx b/docs/content/docs/integrations/astro.mdx
new file mode 100644
index 00000000..a23c7703
--- /dev/null
+++ b/docs/content/docs/integrations/astro.mdx
@@ -0,0 +1,233 @@
+---
+title: Astro
+description: Learn how to use Permix with Astro
+---
+
+## Overview
+
+Permix provides middleware for [Astro](https://astro.build/) through `permix/astro`. It stores a per-request instance on `context.locals`, so Astro middleware, endpoints, and server-rendered pages share the same rules.
+
+UI checks in islands reuse the existing [`permix/react`](/docs/integrations/react), [`permix/vue`](/docs/integrations/vue), [`permix/solid`](/docs/integrations/solid), or [`permix/svelte`](/docs/integrations/svelte) integrations. Dehydrate on the server and hydrate in the island with `PermixHydrate`.
+
+
+ Before getting started with the Astro integration, make sure you've completed
+ the initial setup steps in the [Quick Start](/docs/quick-start) guide.
+
+
+
+
+
+
+## Setup
+
+Create a Permix instance and run `setupMiddleware` from `src/middleware.ts`:
+
+```ts title="src/lib/permix.ts"
+import { createPermix } from 'permix/astro'
+
+interface Post {
+ id: string
+ authorId: string
+}
+
+export const permix = createPermix<{
+ post: [
+ { name: 'create'; type: Post },
+ { name: 'read'; type: Post },
+ { name: 'update'; type: Post },
+ { name: 'delete'; type: Post },
+ ]
+}>()
+```
+
+```ts title="src/middleware.ts"
+import { defineMiddleware } from 'astro:middleware'
+import { permix } from './lib/permix'
+
+export const onRequest = defineMiddleware(
+ permix.setupMiddleware(({ request }) => {
+ const isAdmin = request.headers.get('x-user-role') === 'admin'
+
+ return {
+ post: {
+ create: true,
+ read: true,
+ update: isAdmin,
+ delete: isAdmin,
+ },
+ }
+ })
+)
+```
+
+`setupMiddleware` is compatible with [`sequence`](https://docs.astro.build/en/guides/middleware/#chaining-middleware) if you already have other middleware.
+
+The instance lives on `locals`, not on the `Request` object.
+
+
+
+
+
+## Checking permissions
+
+Use `checkMiddleware` to guard an endpoint, or `getOrThrow` inside the handler for entity-aware checks:
+
+```ts title="src/pages/api/posts.ts"
+import type { APIRoute } from 'astro'
+import { permix } from '../../lib/permix'
+
+export const POST: APIRoute = (context) =>
+ permix.checkMiddleware('post.create')(context, async () => {
+ return Response.json({ ok: true })
+ })
+```
+
+```ts title="src/pages/api/posts/[id].ts"
+import type { APIRoute } from 'astro'
+import { permix } from '../../../lib/permix'
+
+export const PATCH: APIRoute = async (context) => {
+ const post = await getPost(context.params.id)
+
+ if (!permix.getOrThrow(context).check('post.update', post)) {
+ return Response.json({ error: 'Forbidden' }, { status: 403 })
+ }
+
+ return Response.json({ ok: true })
+}
+```
+
+`checkMiddleware` accepts the same arguments as the core `check`: a path, a path plus entity data, or a callback.
+
+Denied requests default to `403` with `{ error: 'Forbidden' }`. Customize with `onForbidden` in `createPermix` options.
+
+
+
+
+
+## Pages and islands
+
+In a `.astro` page, read the instance from `Astro.locals` (or pass `Astro` as the context — `get` accepts either):
+
+```astro title="src/pages/posts/[id].astro"
+---
+import { permix } from '../../lib/permix'
+
+const post = await getPost(Astro.params.id)
+
+if (!permix.getOrThrow(Astro).check('post.read', post)) {
+ return Astro.redirect('/404')
+}
+
+const state = permix.getOrThrow(Astro).dehydrate()
+---
+
+
+```
+
+Hydrate the island with the matching UI adapter:
+
+```tsx title="src/components/EditButton.tsx"
+import { createPermix } from 'permix'
+import { PermixHydrate, PermixProvider, usePermix } from 'permix/react'
+
+const permix = createPermix<{
+ post: [
+ { name: 'create'; type: { id: string; authorId: string } },
+ { name: 'read'; type: { id: string; authorId: string } },
+ { name: 'update'; type: { id: string; authorId: string } },
+ { name: 'delete'; type: { id: string; authorId: string } },
+ ]
+}>()
+
+export function EditButton({
+ post,
+ state,
+}: {
+ post: { id: string; authorId: string }
+ state: ReturnType
+}) {
+ return (
+
+
+
+
+
+ )
+}
+
+function Inner({ post }: { post: { id: string; authorId: string } }) {
+ const { check } = usePermix(permix)
+ if (!check('post.update', post)) return null
+ return Edit post
+}
+```
+
+
+ `hydrate()` restores booleans but does not restore function-based rules or
+ mark the instance ready. Call `permix.setup(...)` on the client with the full
+ rule set (including closures) after hydration. See the [Hydration
+ guide](/docs/guide/hydration).
+
+
+
+
+
+
+## Templates
+
+```ts title="src/lib/permix.ts"
+import { createPermix } from 'permix/astro'
+
+export const permix = createPermix<{
+ post: ['create', 'read', 'update', 'delete']
+}>()
+
+export const adminTemplate = permix.template({
+ post: { create: true, read: true, update: true, delete: true },
+})
+
+export const guestTemplate = permix.template({
+ post: { create: false, read: true, update: false, delete: false },
+})
+```
+
+```ts title="src/middleware.ts"
+import { defineMiddleware } from 'astro:middleware'
+import { adminTemplate, guestTemplate, permix } from './lib/permix'
+
+export const onRequest = defineMiddleware(
+ permix.setupMiddleware(({ request }) => {
+ const isAdmin = request.headers.get('x-user-role') === 'admin'
+ return isAdmin ? adminTemplate() : guestTemplate()
+ })
+)
+```
+
+
+
+
+
+## Example
+
+You can find a runnable example of the Astro integration [here](https://github.com/letstri/permix/tree/main/examples/astro).
+
+
+
+
+
+## API
+
+### `createPermix(options?)`
+
+Returns an object with the following methods:
+
+| Method | Description |
+| --- | --- |
+| `setupMiddleware(rules \| callback)` | Astro middleware that creates a per-request instance on `locals`. |
+| `checkMiddleware(...args)` | Astro middleware that allows or returns the `onForbidden` response. |
+| `get(context \| locals)` | Return the instance, or `null`. |
+| `getOrThrow(context \| locals)` | Return the instance, or throw `PermixNotFoundError`. |
+| `getRules(context \| locals)` | Return the current rules, or `null`. |
+| `template(rules)` | Create a reusable rule set. Same as the core [`template`](/docs/guide/template). |
+| `contextKey(key)` | Store this factory under a custom `locals` key. |
diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json
index dbd79bfc..b05644f9 100644
--- a/docs/content/docs/meta.json
+++ b/docs/content/docs/meta.json
@@ -23,6 +23,7 @@
"integrations/svelte",
"integrations/node",
"integrations/server",
+ "integrations/astro",
"integrations/trpc",
"integrations/orpc",
"integrations/express",
diff --git a/docs/content/docs/migration-v3-to-v4.mdx b/docs/content/docs/migration-v3-to-v4.mdx
index 20be84ca..de876cf4 100644
--- a/docs/content/docs/migration-v3-to-v4.mdx
+++ b/docs/content/docs/migration-v3-to-v4.mdx
@@ -341,6 +341,7 @@ These are additive — migrate the core API first, then adopt what you need:
| `permix/next` | Next.js App Router, request-scoped instance |
| `permix/tanstack-start` | TanStack Start middleware and SSR |
| `permix/server` | Framework-agnostic fetch middleware |
+| `permix/astro` | Astro middleware and `locals` |
| `permix/svelte` | Svelte 5 runes |
| `permix/drizzle` | Rules from Drizzle v1 schema |
| `permix/drizzle/legacy` | Drizzle v0 (`>=0.30 <1`) |
diff --git a/docs/content/docs/quick-start.mdx b/docs/content/docs/quick-start.mdx
index c6bc2196..58be00cd 100644
--- a/docs/content/docs/quick-start.mdx
+++ b/docs/content/docs/quick-start.mdx
@@ -150,6 +150,10 @@ Continuing from the quick start, you can now explore how Permix integrates with
Integration with native Request and Response handlers.
+
+ Integration with Astro middleware and locals.
+
+
Integration with Hono via middleware.
diff --git a/examples/astro/main.ts b/examples/astro/main.ts
new file mode 100644
index 00000000..f9a403a8
--- /dev/null
+++ b/examples/astro/main.ts
@@ -0,0 +1,54 @@
+import { createServer } from 'node:http'
+
+import type { ValidateDefinition } from 'permix'
+import { createPermix } from 'permix/astro'
+
+type PermissionsDefinition = ValidateDefinition<{
+ user: ['read', 'write']
+}>
+
+const permix = createPermix({
+ onForbidden: () =>
+ Response.json(
+ { error: 'You do not have permission to access this resource' },
+ { status: 403 }
+ ),
+})
+
+async function handle(request: Request): Promise {
+ const context = { request, locals: {} }
+
+ return permix.setupMiddleware({
+ user: {
+ read: true,
+ write: false,
+ },
+ })(context, async () => {
+ const url = new URL(request.url)
+
+ if (url.pathname === '/write') {
+ return permix.checkMiddleware('user.write')(context, () =>
+ Response.json({ ok: true })
+ )
+ }
+
+ if (url.pathname === '/permix') {
+ return Response.json({
+ canRead: permix.getOrThrow(context).check('user.read'),
+ })
+ }
+
+ return Response.json({
+ canRead: permix.getOrThrow(context).check('user.read'),
+ })
+ })
+}
+
+createServer(async (req, res) => {
+ const request = new Request(`http://127.0.0.1:3000${req.url ?? '/'}`)
+ const response = await handle(request)
+ res.writeHead(response.status, Object.fromEntries(response.headers))
+ res.end(Buffer.from(await response.arrayBuffer()))
+}).listen(3000, () => {
+ console.log('Server is running on port 3000')
+})
diff --git a/examples/astro/package.json b/examples/astro/package.json
new file mode 100644
index 00000000..422ef367
--- /dev/null
+++ b/examples/astro/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "astro",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "check-types": "tsc --noEmit",
+ "start": "tsx main.ts"
+ },
+ "dependencies": {
+ "permix": "workspace:*"
+ },
+ "devDependencies": {
+ "@types/node": "^25.9.1",
+ "tsx": "^4.22.4"
+ }
+}
diff --git a/examples/astro/tsconfig.json b/examples/astro/tsconfig.json
new file mode 100644
index 00000000..48ce3c61
--- /dev/null
+++ b/examples/astro/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "strict": true,
+ "types": ["node"],
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ }
+}
diff --git a/permix/package.json b/permix/package.json
index dfbefd33..ef771f4f 100644
--- a/permix/package.json
+++ b/permix/package.json
@@ -7,6 +7,7 @@
"keywords": [
"access-control",
"acl",
+ "astro",
"authorization",
"frontend",
"javascript",
@@ -79,6 +80,10 @@
"types": "./dist/server/index.d.mts",
"import": "./dist/server/index.mjs"
},
+ "./astro": {
+ "types": "./dist/astro/index.d.mts",
+ "import": "./dist/astro/index.mjs"
+ },
"./elysia": {
"types": "./dist/elysia/index.d.mts",
"import": "./dist/elysia/index.mjs"
diff --git a/permix/skills/permix/SKILL.md b/permix/skills/permix/SKILL.md
index b3ebc1b5..b4b7acee 100644
--- a/permix/skills/permix/SKILL.md
+++ b/permix/skills/permix/SKILL.md
@@ -1,7 +1,7 @@
---
name: permix
description: >-
- Applies Permix authorization once a schema exists: permix.check() paths and ReBAC callbacks, frontend bindings (permix/react, permix/vue, permix/solid, permix/svelte) with SSR dehydrate/hydrate for Next.js and TanStack Start, and server middleware (permix/express, hono, fastify, trpc, orpc, node, elysia). Use for anything past initial setup — checking permissions, gating UI, or protecting routes. For creating the schema and first `permix.setup()`, use permix-getting-started first.
+ Applies Permix authorization once a schema exists: permix.check() paths and ReBAC callbacks, frontend bindings (permix/react, permix/vue, permix/solid, permix/svelte) with SSR dehydrate/hydrate for Next.js and TanStack Start, and server middleware (permix/express, hono, fastify, trpc, orpc, node, elysia, astro). Use for anything past initial setup — checking permissions, gating UI, or protecting routes. For creating the schema and first `permix.setup()`, use permix-getting-started first.
metadata:
type: core
library: permix
@@ -26,6 +26,7 @@ sources:
- 'letstri/permix:docs/content/docs/integrations/orpc.mdx'
- 'letstri/permix:docs/content/docs/integrations/node.mdx'
- 'letstri/permix:docs/content/docs/integrations/server.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/astro.mdx'
- 'letstri/permix:docs/content/docs/integrations/elysia.mdx'
- 'letstri/permix:permix/src/core/check.ts'
---
@@ -38,7 +39,7 @@ Assumes a `permix` instance already exists (see **permix-getting-started**). Loa
| --- | --- |
| `permix.check()` paths, callbacks, `~all`/`~any`, ReBAC/ABAC with entity data, `isReady` | [references/check.md](references/check.md) |
| React, Vue, Solid, or Svelte UI — `PermixProvider`, `usePermix`, `createComponents`, SSR `dehydrate`/`hydrate` for Next.js / TanStack Start | [references/frontend.md](references/frontend.md) |
-| Protecting Express, Hono, Fastify, tRPC, oRPC, Node, or Elysia routes — `setupMiddleware`, `checkMiddleware` | [references/server.md](references/server.md) |
+| Protecting Express, Hono, Fastify, tRPC, oRPC, Node, Elysia, or Astro routes — `setupMiddleware`, `checkMiddleware` | [references/server.md](references/server.md) |
## Rules that apply everywhere
diff --git a/permix/skills/permix/references/server.md b/permix/skills/permix/references/server.md
index 8b8c79d5..74590619 100644
--- a/permix/skills/permix/references/server.md
+++ b/permix/skills/permix/references/server.md
@@ -80,6 +80,7 @@ app.get('/posts/:id', (req, res) => {
| tRPC | `permix/trpc` |
| oRPC | `permix/orpc` |
| Generic HTTP | `permix/node` or `permix/server` |
+| Astro | `permix/astro` |
| Elysia | `permix/elysia` |
| Effect | `permix/effect` — see integration docs |
| Drizzle ORM | `permix/drizzle` (and `permix/drizzle/legacy`) — see integration docs |
diff --git a/permix/src/astro/index.ts b/permix/src/astro/index.ts
new file mode 100644
index 00000000..60dafabb
--- /dev/null
+++ b/permix/src/astro/index.ts
@@ -0,0 +1 @@
+export * from './permix'
diff --git a/permix/src/astro/permix.test.ts b/permix/src/astro/permix.test.ts
new file mode 100644
index 00000000..8ae85cd6
--- /dev/null
+++ b/permix/src/astro/permix.test.ts
@@ -0,0 +1,399 @@
+import { describe, expect, it, vi } from 'vitest'
+
+import type { ValidateDefinition } from '../core'
+import { PermixNotFoundError } from '../core'
+import { createPermix } from './permix'
+import type { AstroContext } from './permix'
+
+interface Post {
+ id: string
+ authorId: string
+}
+
+type PermissionsDefinition = ValidateDefinition<{
+ post: ['create', 'read', 'update']
+ user: ['delete']
+}>
+
+type PostWithData = ValidateDefinition<{
+ post: [{ name: 'create'; type: Post }]
+}>
+
+function createMockContext(): AstroContext {
+ return {
+ request: new Request('https://example.com'),
+ locals: {},
+ }
+}
+
+function createMockNext(response = new Response('ok')) {
+ return vi.fn(() => response)
+}
+
+describe(createPermix, () => {
+ const permix = createPermix()
+
+ it('should throw ts error', () => {
+ // @ts-expect-error path does not exist
+ permix.checkMiddleware('post.delete')
+ })
+
+ it('should allow access when permission is granted', async () => {
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware({
+ post: { create: true, read: false, update: false },
+ user: { delete: false },
+ })(context, next)
+
+ const result = await permix.checkMiddleware('post.create')(context, next)
+
+ expect(result?.status).toBe(200)
+ expect(next).toHaveBeenCalledTimes(2)
+ expect(next).toHaveBeenLastCalledWith()
+ })
+
+ it('should deny access when permission is not granted', async () => {
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware({
+ post: { create: false, read: false, update: false },
+ user: { delete: false },
+ })(context, next)
+
+ const result = await permix.checkMiddleware('post.create')(context, next)
+
+ expect(result?.status).toBe(403)
+ await expect(result?.text()).resolves.toBe(
+ JSON.stringify({ error: 'Forbidden' })
+ )
+ })
+
+ it('should work with custom error handler', async () => {
+ const permix = createPermix({
+ onForbidden: () =>
+ new Response(JSON.stringify({ error: 'Custom error' }), {
+ status: 403,
+ headers: { 'Content-Type': 'application/json' },
+ }),
+ })
+
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware({
+ post: { create: false, read: false, update: false },
+ user: { delete: false },
+ })(context, next)
+
+ const result = await permix.checkMiddleware('post.create')(context, next)
+
+ expect(result?.status).toBe(403)
+ await expect(result?.text()).resolves.toBe(
+ JSON.stringify({ error: 'Custom error' })
+ )
+ })
+
+ it('should work with custom error and params', async () => {
+ const permix = createPermix({
+ onForbidden: ({ path }) =>
+ new Response(
+ JSON.stringify({ error: `You do not have permission for ${path}` }),
+ {
+ status: 403,
+ headers: { 'Content-Type': 'application/json' },
+ }
+ ),
+ })
+
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware({
+ post: { create: false, read: false, update: false },
+ user: { delete: false },
+ })(context, next)
+
+ const result = await permix.checkMiddleware('post.create')(context, next)
+
+ expect(result?.status).toBe(403)
+ await expect(result?.text()).resolves.toBe(
+ JSON.stringify({ error: 'You do not have permission for post.create' })
+ )
+ })
+
+ it('should pass data through to a rule callback', async () => {
+ const permix = createPermix()
+
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware({
+ post: {
+ create: (post) => post?.authorId === '1',
+ },
+ })(context, next)
+
+ const result = await permix.checkMiddleware('post.create', {
+ id: 'a',
+ authorId: '1',
+ })(context, next)
+
+ expect(result?.status).toBe(200)
+ })
+
+ it('should work with checker callback form', async () => {
+ const permix = createPermix()
+
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware({
+ post: { create: true, read: true, update: false },
+ user: { delete: true },
+ })(context, next)
+
+ const result = await permix.checkMiddleware(
+ (c) => c('post.create') && c('user.delete')
+ )(context, next)
+
+ expect(result?.status).toBe(200)
+ })
+
+ it('should work with template', async () => {
+ const template = permix.template({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ })
+
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware(() => template())(context, next)
+
+ const result = await permix.checkMiddleware('post.create')(context, next)
+
+ expect(result?.status).toBe(200)
+ })
+
+ it('should dehydrate permissions', async () => {
+ const template = permix.template({
+ post: { create: true, read: false, update: true },
+ user: { delete: false },
+ })
+
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware(() => template())(context, next)
+
+ expect(permix.getOrThrow(context).dehydrate()).toStrictEqual({
+ post: { create: true, read: false, update: true },
+ user: { delete: false },
+ })
+ })
+
+ it('should read the instance from locals as well as context', async () => {
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ })(context, next)
+
+ expect(permix.get(context.locals)?.check('post.create')).toBe(true)
+ expect(permix.getOrThrow(context.locals).check('post.create')).toBe(true)
+ })
+
+ it('should let two factories with different keys coexist on the same request', async () => {
+ const admin = createPermix().contextKey('admin')
+ const guest = createPermix().contextKey('guest')
+
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await admin.setupMiddleware({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ })(context, next)
+ await guest.setupMiddleware({
+ post: { create: false, read: true, update: false },
+ user: { delete: false },
+ })(context, next)
+
+ const adminNext = createMockNext()
+ const adminResult = await admin.checkMiddleware('post.create')(
+ context,
+ adminNext
+ )
+ expect(adminResult?.status).toBe(200)
+ expect(adminNext).toHaveBeenCalledWith()
+
+ const guestNext = createMockNext()
+ const guestResult = await guest.checkMiddleware('post.create')(
+ context,
+ guestNext
+ )
+ expect(guestResult?.status).toBe(403)
+ expect(guestNext).not.toHaveBeenCalled()
+ })
+
+ it('should default to a per-instance symbol so two factories without a key do not collide', async () => {
+ const first = createPermix()
+ const second = createPermix()
+
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await first.setupMiddleware({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ })(context, next)
+ await second.setupMiddleware({
+ post: { create: false, read: false, update: false },
+ user: { delete: false },
+ })(context, next)
+
+ const firstNext = createMockNext()
+ const firstResult = await first.checkMiddleware('post.create')(
+ context,
+ firstNext
+ )
+ expect(firstResult?.status).toBe(200)
+
+ const secondNext = createMockNext()
+ const secondResult = await second.checkMiddleware('post.create')(
+ context,
+ secondNext
+ )
+ expect(secondResult?.status).toBe(403)
+ })
+
+ it('should accept an explicit symbol key', async () => {
+ const key = Symbol('my-permix')
+ const permix = createPermix().contextKey(key)
+
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ })(context, next)
+
+ expect(Boolean((context.locals as Record)[key])).toBe(
+ true
+ )
+ })
+})
+
+describe('get / getOrThrow', () => {
+ const permix = createPermix()
+
+ it('should return null when setupMiddleware has not run', () => {
+ const context = createMockContext()
+ expect(permix.get(context)).toBeNull()
+ })
+
+ it('should return the instance when setupMiddleware has run', async () => {
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ })(context, next)
+
+ const p = permix.getOrThrow(context)
+ expect(p.check).toBeTypeOf('function')
+ })
+
+ it('getOrThrow should throw PermixNotFoundError when missing', () => {
+ const context = createMockContext()
+ expect(() => permix.getOrThrow(context)).toThrow(PermixNotFoundError)
+ })
+})
+
+describe('checkMiddleware without setupMiddleware', () => {
+ it('should throw PermixNotFoundError', async () => {
+ const permix = createPermix()
+
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await expect(
+ permix.checkMiddleware('post.create')(context, next)
+ ).rejects.toBeInstanceOf(PermixNotFoundError)
+ expect(next).not.toHaveBeenCalled()
+ })
+})
+
+describe('onForbidden receives next', () => {
+ it('should allow onForbidden to throw custom errors', async () => {
+ const permix = createPermix({
+ onForbidden: ({ path }) => {
+ throw new Error(`Forbidden: ${path}`)
+ },
+ })
+
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware({
+ post: { create: false, read: false, update: false },
+ user: { delete: false },
+ })(context, next)
+
+ await expect(
+ permix.checkMiddleware('post.create')(context, next)
+ ).rejects.toMatchObject({
+ message: 'Forbidden: post.create',
+ })
+ })
+})
+
+describe('Astro-style middleware composition', () => {
+ it('should compose setup and check middleware like sequence()', async () => {
+ const permix = createPermix()
+
+ const middleware = [
+ permix.setupMiddleware({
+ post: { create: true, read: false, update: false },
+ user: { delete: false },
+ }),
+ permix.checkMiddleware('post.create'),
+ ]
+
+ const run = async (context: AstroContext) => {
+ let index = 0
+ const dispatch = async (): Promise => {
+ const handler = middleware[index++]
+ if (handler) {
+ return await handler(context, dispatch)
+ }
+ return Response.json({ ok: true })
+ }
+ return await dispatch()
+ }
+
+ const res = await run(createMockContext())
+ expect(res.status).toBe(200)
+ await expect(res.json()).resolves.toStrictEqual({ ok: true })
+ })
+})
+
+describe('key exposure', () => {
+ it('should expose the key on the factory return', () => {
+ const permix =
+ createPermix().contextKey('custom-key')
+ expect(permix.key).toBe('custom-key')
+ })
+
+ it('should expose a symbol key when using default', () => {
+ const permix = createPermix()
+ expect(permix.key).toBeTypeOf('symbol')
+ })
+})
diff --git a/permix/src/astro/permix.ts b/permix/src/astro/permix.ts
new file mode 100644
index 00000000..9f2731d7
--- /dev/null
+++ b/permix/src/astro/permix.ts
@@ -0,0 +1,212 @@
+import type { Permix as PermixCore } from '../core'
+import {
+ createCheckContext,
+ createHooks,
+ createPermix as createPermixCore,
+ createTemplate,
+ PermixNotFoundError,
+} from '../core'
+import type { CheckArgs, CheckContext } from '../core/check'
+import type { Definition } from '../core/definitions'
+import type { PermixHooks, Rules, RulesPaths } from '../core/permix'
+import type { MaybePromise } from '../utils'
+
+/**
+ * Minimal Astro `locals` bag. Compatible with `App.Locals`.
+ */
+export type AstroLocals = object
+
+/**
+ * Minimal Astro middleware / endpoint context. Compatible with `APIContext`
+ * from `astro`.
+ */
+export interface AstroContext {
+ request: Request
+ locals: AstroLocals
+}
+
+export type MiddlewareNext = () => MaybePromise
+
+/**
+ * Astro middleware: `(context, next) => Response`. Compatible with
+ * `defineMiddleware` and `sequence` from `astro:middleware`.
+ */
+export type AstroMiddleware = (
+ context: AstroContext,
+ next: MiddlewareNext
+) => MaybePromise
+
+export interface MiddlewareContext {
+ context: AstroContext
+ request: Request
+ locals: AstroLocals
+ next: MiddlewareNext
+}
+
+export interface PermixOptions {
+ /**
+ * Called when a `checkMiddleware` denies the request. Defaults to a 403 JSON
+ * response of `{ error: 'Forbidden' }`.
+ */
+ onForbidden?: (
+ params: CheckContext & MiddlewareContext
+ ) => MaybePromise
+}
+
+function isContext(source: AstroContext | AstroLocals): source is AstroContext {
+ return (
+ typeof source === 'object' &&
+ source !== null &&
+ 'locals' in source &&
+ 'request' in source
+ )
+}
+
+function readLocals(source: AstroContext | AstroLocals): AstroLocals {
+ return isContext(source) ? source.locals : source
+}
+
+function buildPermix(
+ resolveKey: () => string | symbol,
+ options: PermixOptions = {}
+) {
+ const onForbidden =
+ options.onForbidden ??
+ (() =>
+ new Response(JSON.stringify({ error: 'Forbidden' }), {
+ status: 403,
+ headers: { 'Content-Type': 'application/json' },
+ }))
+
+ const hooks = createHooks>()
+
+ function get(source: AstroContext | AstroLocals): PermixCore | null {
+ const locals = readLocals(source) as Record
+ const instance = locals[resolveKey()] as PermixCore | undefined
+ return instance ?? null
+ }
+
+ function getOrThrow(source: AstroContext | AstroLocals): PermixCore {
+ const instance = get(source)
+ if (!instance) {
+ throw new PermixNotFoundError(resolveKey())
+ }
+ return instance
+ }
+
+ function setupMiddleware(
+ callbackOrRules:
+ | ((context: MiddlewareContext) => MaybePromise>)
+ | Rules
+ ): AstroMiddleware {
+ return async (context, next) => {
+ const rules =
+ typeof callbackOrRules === 'function'
+ ? await callbackOrRules({
+ context,
+ request: context.request,
+ locals: context.locals,
+ next,
+ })
+ : callbackOrRules
+ const instance = createPermixCore(rules)
+ instance.hook('check', (checkContext) => {
+ hooks.callHook('check', checkContext)
+ })
+ const locals = context.locals as Record
+ locals[resolveKey()] = instance
+ return await next()
+ }
+ }
+
+ const checkMiddleware: (...args: CheckArgs) => AstroMiddleware =
+ (...args) =>
+ async (context, next) => {
+ const permix = get(context)
+
+ if (!permix) {
+ throw new PermixNotFoundError(resolveKey())
+ }
+
+ const allowed = permix.check(...args)
+
+ if (!allowed) {
+ return await onForbidden({
+ context,
+ request: context.request,
+ locals: context.locals,
+ next,
+ ...createCheckContext(...args),
+ })
+ }
+
+ return await next()
+ }
+
+ function getRules(source: AstroContext | AstroLocals): Rules | null {
+ return get(source)?.getRules() ?? null
+ }
+
+ function template(rules: Rules | ((param: T) => Rules)) {
+ return createTemplate(rules)
+ }
+
+ return {
+ setupMiddleware,
+ checkMiddleware,
+ template,
+ get,
+ getOrThrow,
+ getRules,
+ hook: hooks.hook,
+ hookOnce: hooks.hookOnce,
+ get key() {
+ return resolveKey()
+ },
+ $inferDefinition: undefined as unknown as D,
+ $inferPath: undefined as unknown as RulesPaths,
+ }
+}
+
+/**
+ * Create a middleware factory that wires Permix into Astro.
+ *
+ * The instance is stored on `context.locals`, so middleware, endpoints, and
+ * server-rendered pages in the same request share one instance.
+ *
+ * @example
+ * ```ts
+ * // src/middleware.ts
+ * import { defineMiddleware } from 'astro:middleware'
+ * import { createPermix } from 'permix/astro'
+ *
+ * export const permix = createPermix<{
+ * post: ['create', 'read', 'update', 'delete']
+ * }>()
+ *
+ * export const onRequest = defineMiddleware(
+ * permix.setupMiddleware(({ request }) => ({
+ * post: { create: true, read: true, update: false, delete: false },
+ * })),
+ * )
+ * ```
+ *
+ * @link https://permix.letstri.dev/docs/integrations/astro
+ */
+export function createPermix(
+ options: PermixOptions = {}
+) {
+ let key: string | symbol = Symbol('permix')
+ const permix = buildPermix(() => key, options)
+
+ return Object.assign(permix, {
+ contextKey(newKey: string | symbol) {
+ key = newKey
+ return permix
+ },
+ })
+}
+
+export type AstroPermix = ReturnType<
+ typeof createPermix
+>
diff --git a/permix/tsdown.config.ts b/permix/tsdown.config.ts
index 00e9a0a3..4822174e 100644
--- a/permix/tsdown.config.ts
+++ b/permix/tsdown.config.ts
@@ -14,6 +14,7 @@ export default defineConfig({
'./src/hono/index.ts',
'./src/node/index.ts',
'./src/server/index.ts',
+ './src/astro/index.ts',
'./src/elysia/index.ts',
'./src/fastify/index.ts',
'./src/solid/index.ts',
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 761d130a..1fa750af 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -136,6 +136,19 @@ importers:
specifier: ^6.0.3
version: 6.0.3
+ examples/astro:
+ dependencies:
+ permix:
+ specifier: workspace:*
+ version: link:../../permix
+ devDependencies:
+ '@types/node':
+ specifier: ^25.9.1
+ version: 25.9.1
+ tsx:
+ specifier: ^4.22.4
+ version: 4.22.4
+
examples/enum-based:
dependencies:
permix:
@@ -435,7 +448,7 @@ importers:
version: 1.167.1(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-router-ssr-query':
specifier: latest
- version: 1.167.1(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ version: 1.167.2(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start':
specifier: latest
version: 1.168.49(crossws@0.4.5(srvx@0.11.16))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.13)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -548,7 +561,7 @@ importers:
version: 4.12.23
next:
specifier: '>=14'
- version: 16.2.6(@babel/core@7.29.7)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ version: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react:
specifier: '>=18'
version: 19.2.6
@@ -2634,12 +2647,12 @@ packages:
'@tanstack/router-core':
optional: true
- '@tanstack/react-router-ssr-query@1.167.1':
- resolution: {integrity: sha512-W9j5JPnBikyafvuUfykFfHIWod58OAbAAa5leNkXBcoDoocghMmu6w9uZOmUZvAWT7CSvgj5tBUtF7CM2OoHXQ==}
+ '@tanstack/react-router-ssr-query@1.167.2':
+ resolution: {integrity: sha512-yRvy0VJ00R8huPuquk+qyYQGMpYRc/G33mc6/yZ4f7LaBZz9h/VbACjmLzhm/+6u5WSmUt6ouJJZnI1/5Npfag==}
engines: {node: '>=20.19'}
peerDependencies:
- '@tanstack/query-core': '>=5.90.0'
- '@tanstack/react-query': '>=5.90.0'
+ '@tanstack/query-core': '>=5.102.0'
+ '@tanstack/react-query': '>=5.102.0'
'@tanstack/react-router': '>=1.127.0'
react: '>=18.0.0 || >=19.0.0'
react-dom: '>=18.0.0 || >=19.0.0'
@@ -2927,11 +2940,11 @@ packages:
webpack:
optional: true
- '@tanstack/router-ssr-query-core@1.169.1':
- resolution: {integrity: sha512-rngux8s/3mPQzcjLYDLkNU31coYVyCgrVTfpdwqUdY5jIEHqGTXrO73DTkPR1PppwYUeVhmNCgl8TctRcnupjg==}
+ '@tanstack/router-ssr-query-core@1.169.2':
+ resolution: {integrity: sha512-7pO65Aiq/1+aS3Mb6vSGtIjzQ/YGv9JTfBbn2EYlHcZnJ4s4ZGCeTPI847geB1RfJqVhfwxM8bZgGM51mYkolg==}
engines: {node: '>=20.19'}
peerDependencies:
- '@tanstack/query-core': '>=5.90.0'
+ '@tanstack/query-core': '>=5.102.0'
'@tanstack/router-core': '>=1.127.0'
'@tanstack/router-utils@1.162.1':
@@ -8665,12 +8678,12 @@ snapshots:
transitivePeerDependencies:
- csstype
- '@tanstack/react-router-ssr-query@1.167.1(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
+ '@tanstack/react-router-ssr-query@1.167.2(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.6))(@tanstack/react-router@1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.27)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
'@tanstack/query-core': 5.101.0
'@tanstack/react-query': 5.101.0(react@19.2.6)
'@tanstack/react-router': 1.170.32(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
- '@tanstack/router-ssr-query-core': 1.169.1(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)
+ '@tanstack/router-ssr-query-core': 1.169.2(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
transitivePeerDependencies:
@@ -9062,7 +9075,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tanstack/router-ssr-query-core@1.169.1(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)':
+ '@tanstack/router-ssr-query-core@1.169.2(@tanstack/query-core@5.101.0)(@tanstack/router-core@1.171.27)':
dependencies:
'@tanstack/query-core': 5.101.0
'@tanstack/router-core': 1.171.27
@@ -9673,7 +9686,7 @@ snapshots:
obug: 2.1.1
std-env: 4.1.0
tinyrainbow: 3.1.0
- vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitest: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(happy-dom@20.9.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vitest/expect@4.1.8':
dependencies:
@@ -11982,30 +11995,6 @@ snapshots:
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- next@16.2.6(@babel/core@7.29.7)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
- dependencies:
- '@next/env': 16.2.6
- '@swc/helpers': 0.5.15
- baseline-browser-mapping: 2.10.33
- caniuse-lite: 1.0.30001793
- postcss: 8.4.31
- react: 19.2.6
- react-dom: 19.2.6(react@19.2.6)
- styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.6)
- optionalDependencies:
- '@next/swc-darwin-arm64': 16.2.6
- '@next/swc-darwin-x64': 16.2.6
- '@next/swc-linux-arm64-gnu': 16.2.6
- '@next/swc-linux-arm64-musl': 16.2.6
- '@next/swc-linux-x64-gnu': 16.2.6
- '@next/swc-linux-x64-musl': 16.2.6
- '@next/swc-win32-arm64-msvc': 16.2.6
- '@next/swc-win32-x64-msvc': 16.2.6
- sharp: 0.34.5
- transitivePeerDependencies:
- - '@babel/core'
- - babel-plugin-macros
-
next@16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies:
'@next/env': 16.2.6
@@ -12039,7 +12028,7 @@ snapshots:
postcss: 8.4.31
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.6)
+ styled-jsx: 5.1.6(react@19.2.6)
optionalDependencies:
'@next/swc-darwin-arm64': 16.2.6
'@next/swc-darwin-x64': 16.2.6
@@ -12053,7 +12042,6 @@ snapshots:
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
- optional: true
nf3@0.3.17: {}
@@ -12946,17 +12934,15 @@ snapshots:
dependencies:
inline-style-parser: 0.2.7
- styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.6):
+ styled-jsx@5.1.6(react@19.2.4):
dependencies:
client-only: 0.0.1
- react: 19.2.6
- optionalDependencies:
- '@babel/core': 7.29.7
+ react: 19.2.4
- styled-jsx@5.1.6(react@19.2.4):
+ styled-jsx@5.1.6(react@19.2.6):
dependencies:
client-only: 0.0.1
- react: 19.2.4
+ react: 19.2.6
stylis@4.4.0: {}
From b49e7de7bae5d652bab44253b24be18d4206b603 Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 15:11:58 +0200
Subject: [PATCH 14/28] feat: add React Router adapter with middleware context
isolation
React Router 7 (including Remix) needs a per-request instance on middleware context so loaders can dehydrate into permix/react.
Co-authored-by: Cursor
---
_artifacts/domain_map.yaml | 9 +-
_artifacts/skill_tree.yaml | 5 +-
docs/content/docs/comparison.mdx | 3 +-
docs/content/docs/integrations/next.mdx | 4 +-
.../docs/integrations/react-router.mdx | 255 ++++++++++++++
docs/content/docs/integrations/react.mdx | 2 +-
docs/content/docs/meta.json | 1 +
docs/content/docs/migration-v3-to-v4.mdx | 3 +-
docs/content/docs/quick-start.mdx | 4 +
examples/react-router/main.ts | 59 ++++
examples/react-router/package.json | 16 +
examples/react-router/tsconfig.json | 11 +
permix/package.json | 5 +
permix/skills/permix/SKILL.md | 5 +-
permix/skills/permix/references/frontend.md | 6 +-
permix/src/react-router/index.ts | 1 +
permix/src/react-router/permix.test.ts | 331 ++++++++++++++++++
permix/src/react-router/permix.ts | 216 ++++++++++++
permix/tsdown.config.ts | 1 +
pnpm-lock.yaml | 13 +
20 files changed, 937 insertions(+), 13 deletions(-)
create mode 100644 docs/content/docs/integrations/react-router.mdx
create mode 100644 examples/react-router/main.ts
create mode 100644 examples/react-router/package.json
create mode 100644 examples/react-router/tsconfig.json
create mode 100644 permix/src/react-router/index.ts
create mode 100644 permix/src/react-router/permix.test.ts
create mode 100644 permix/src/react-router/permix.ts
diff --git a/_artifacts/domain_map.yaml b/_artifacts/domain_map.yaml
index 318aa7c9..c1823ab9 100644
--- a/_artifacts/domain_map.yaml
+++ b/_artifacts/domain_map.yaml
@@ -39,7 +39,7 @@ domains:
- name: 'SSR and hydration'
slug: ssr
description: >
- dehydrate/hydrate snapshots, PermixHydrate, Next.js and TanStack Start wiring.
+ dehydrate/hydrate snapshots, PermixHydrate, Next.js, TanStack Start, and React Router wiring.
skills:
- name: 'Getting started'
@@ -77,7 +77,7 @@ skills:
Everything past initial setup: dot-path check, callback combinators,
~all/~any, entity-aware ReBAC rules, isReady/isReadyAsync;
PermixProvider/usePermix/createComponents and SSR dehydrate/hydrate for
- React, Vue, Solid, Svelte, Next.js, TanStack Start; setupMiddleware and
+ React, Vue, Solid, Svelte, Next.js, TanStack Start, React Router; setupMiddleware and
checkMiddleware for Express, Hono, Fastify, tRPC, oRPC, Node, Elysia.
Single skill with a thin SKILL.md router and three reference files
loaded on demand.
@@ -91,6 +91,7 @@ skills:
- svelte
- next
- tanstack-start
+ - react-router
- express
- hono
- fastify
@@ -119,7 +120,7 @@ skills:
- 'Wrap the app tree and hide actions based on permissions'
- 'Integrate permix/react, permix/vue, permix/solid, or permix/svelte'
- 'Pass permission booleans from server render to client'
- - 'Wire permix/next or permix/tanstack-start'
+ - 'Wire permix/next, permix/tanstack-start, or permix/react-router'
- 'Protect HTTP or RPC routes on the server'
- 'Derive rules from authenticated request context'
references:
@@ -137,6 +138,7 @@ skills:
- 'letstri/permix:docs/content/docs/integrations/svelte.mdx'
- 'letstri/permix:docs/content/docs/integrations/next.mdx'
- 'letstri/permix:docs/content/docs/integrations/tanstack-start.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/react-router.mdx'
- 'letstri/permix:docs/content/docs/integrations/express.mdx'
- 'letstri/permix:docs/content/docs/integrations/hono.mdx'
- 'letstri/permix:docs/content/docs/integrations/fastify.mdx'
@@ -153,6 +155,7 @@ coverage:
- examples/*
- next
- tanstack-start
+ - react-router
failure_modes:
- mistake: 'Using v3 { action, dataType } schema shape'
diff --git a/_artifacts/skill_tree.yaml b/_artifacts/skill_tree.yaml
index 70b42866..2d1d6a0d 100644
--- a/_artifacts/skill_tree.yaml
+++ b/_artifacts/skill_tree.yaml
@@ -39,7 +39,7 @@ skills:
check() dot paths, callbacks, ~all/~any, entity-aware ReBAC rules,
isReady/isReadyAsync; PermixProvider/usePermix/createComponents and SSR
dehydrate/hydrate for React, Vue, Solid, Svelte, Next.js, TanStack
- Start; setupMiddleware/checkMiddleware for Express, Hono, Fastify,
+ Start, React Router; setupMiddleware/checkMiddleware for Express, Hono, Fastify,
tRPC, oRPC, Node, Elysia. Thin router with references loaded on demand.
requires:
- permix-getting-started
@@ -50,6 +50,7 @@ skills:
- svelte
- next
- tanstack-start
+ - react-router
- express
- hono
- fastify
@@ -72,6 +73,7 @@ skills:
- 'letstri/permix:docs/content/docs/integrations/svelte.mdx'
- 'letstri/permix:docs/content/docs/integrations/next.mdx'
- 'letstri/permix:docs/content/docs/integrations/tanstack-start.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/react-router.mdx'
- 'letstri/permix:docs/content/docs/integrations/express.mdx'
- 'letstri/permix:docs/content/docs/integrations/hono.mdx'
- 'letstri/permix:docs/content/docs/integrations/fastify.mdx'
@@ -88,3 +90,4 @@ coverage:
- examples/*
- next
- tanstack-start
+ - react-router
diff --git a/docs/content/docs/comparison.mdx b/docs/content/docs/comparison.mdx
index d0d9ea67..760b2971 100644
--- a/docs/content/docs/comparison.mdx
+++ b/docs/content/docs/comparison.mdx
@@ -19,7 +19,7 @@ Permix is a library that provides a way to manage permissions in your applicatio
| Events | ✅ | ❌ |
| Simple DX | ✅ Create instance, use built-in integrations | ❌ In CASL you need to manage a lot of stuff manually (type-safe, hydration, etc.) |
| Modernity | ✅ Uses modern updates and features of each lib and framework | ❌ CASL was created a long time ago and hasn't updated the core |
-| Size | **2.64 kB** gzip (core) [react 0.86 kB, vue 0.87 kB, solid 0.82 kB, next 1.00 kB, tanstack-start 2.05 kB, svelte ~2.17 kB, …] | **6.17 kB** min+gzip (core) [@casl/react 0.62 kB] |
+| Size | **2.64 kB** gzip (core) [react 0.86 kB, vue 0.87 kB, solid 0.82 kB, next 1.00 kB, react-router 1.10 kB, tanstack-start 2.05 kB, svelte ~2.17 kB, …] | **6.17 kB** min+gzip (core) [@casl/react 0.62 kB] |
Sizes are hard numbers from published builds — see [Bundle size](#bundle-size). Bracketed values are integration adapters imported on top of core.
@@ -40,6 +40,7 @@ Built with `pnpm run build` in the `permix` package (`tsdown` for all entries ex
| `permix/svelte` | ~2.17 kB | adapter (`dist/svelte/`, `svelte-package` build) |
| `permix/next` | 1.00 kB | adapter |
| `permix/tanstack-start` | 2.05 kB | adapter |
+| `permix/react-router` | 1.10 kB | adapter |
| `permix/node` | 0.95 kB | adapter |
| `permix/server` | 1.10 kB | adapter |
| `permix/express` | 0.91 kB | adapter |
diff --git a/docs/content/docs/integrations/next.mdx b/docs/content/docs/integrations/next.mdx
index 5f1f1204..298ad0dd 100644
--- a/docs/content/docs/integrations/next.mdx
+++ b/docs/content/docs/integrations/next.mdx
@@ -322,8 +322,8 @@ Returns an object with the following methods:
## TanStack Start and other frameworks
-The client layer (`permix/react`) is framework-agnostic. If you're using TanStack Start, Remix, or a custom React SSR setup, you can still use `permix/react` on the client. For the server side, either:
+The client layer (`permix/react`) is framework-agnostic. If you're using TanStack Start, React Router 7, or a custom React SSR setup, you can still use `permix/react` on the client. For the server side, either:
-- Use the dedicated [`permix/tanstack-start`](/docs/integrations/tanstack-start) integration, which follows the same shape as `permix/next`, or
+- Use the dedicated [`permix/tanstack-start`](/docs/integrations/tanstack-start) or [`permix/react-router`](/docs/integrations/react-router) integration, which follow the same shape as `permix/next`, or
- Create a core Permix instance per request manually (see [Hydration guide](/docs/guide/hydration)), or
- Use an existing server integration like [`permix/node`](/docs/integrations/node), [`permix/express`](/docs/integrations/express), or [`permix/hono`](/docs/integrations/hono) when applicable.
diff --git a/docs/content/docs/integrations/react-router.mdx b/docs/content/docs/integrations/react-router.mdx
new file mode 100644
index 00000000..0a4f61d6
--- /dev/null
+++ b/docs/content/docs/integrations/react-router.mdx
@@ -0,0 +1,255 @@
+---
+title: React Router
+description: Learn how to use Permix with React Router 7
+---
+
+## Overview
+
+Permix provides a dedicated integration for [React Router 7](https://reactrouter.com/) through `permix/react-router`. Remix apps that have moved to React Router 7 use this same adapter — there is no separate `permix/remix` export.
+
+It stores a **per-request** Permix instance on React Router's middleware context (`context.set` / `context.get`), so loaders, actions, and middleware share one instance. The client reuses [`permix/react`](/docs/integrations/react): dehydrate on the server and hydrate with `PermixProvider` + `PermixHydrate`.
+
+
+ Before getting started, complete the [Quick Start](/docs/quick-start). This
+ adapter uses [React Router
+ middleware](https://reactrouter.com/how-to/middleware) (React Router 7.9+).
+ Enable `v8_middleware` (or the current middleware flag) in your React Router
+ config if it is not on by default.
+
+
+
+
+
+
+## Define your permissions
+
+```ts title="app/lib/permix.ts"
+import type { ValidateDefinition } from 'permix'
+import { createPermix } from 'permix/react-router'
+
+interface Post {
+ id: string
+ authorId: string
+}
+
+export type PermissionsDefinition = ValidateDefinition<{
+ post: [
+ { name: 'create'; type: Post },
+ { name: 'read'; type: Post },
+ { name: 'update'; type: Post },
+ { name: 'delete'; type: Post },
+ ]
+}>
+
+export const permix = createPermix()
+```
+
+Each `createPermix()` call uses its own context key, so two factories on the same request do not collide.
+
+
+
+
+
+## Setup per request
+
+Register `setupMiddleware` on the root route so every request gets a fresh instance:
+
+```ts title="app/root.tsx"
+import { permix } from './lib/permix'
+import { getSession } from './lib/auth'
+
+export const middleware = [
+ permix.setupMiddleware(async ({ request }) => {
+ const session = await getSession(request)
+
+ return {
+ post: {
+ create: !!session,
+ read: true,
+ update: (post) => post?.authorId === session?.userId,
+ delete: session?.role === 'admin',
+ },
+ }
+ }),
+]
+```
+
+You can also attach it to a specific route's `middleware` array instead of the root.
+
+
+
+
+
+## Check in loaders and actions
+
+```ts title="app/routes/posts.$id.tsx"
+import { data } from 'react-router'
+import { permix } from '../lib/permix'
+import { getPost } from '../lib/posts'
+import type { Route } from './+types/posts.$id'
+
+export async function loader({ params, context }: Route.LoaderArgs) {
+ const post = await getPost(params.id)
+
+ if (!permix.getOrThrow(context).check('post.read', post)) {
+ throw data('Not Found', { status: 404 })
+ }
+
+ return { post, permixState: permix.dehydrate(context) }
+}
+
+export async function action({ context }: Route.ActionArgs) {
+ if (!permix.getOrThrow(context).check('post.create')) {
+ throw data({ error: 'Forbidden' }, { status: 403 })
+ }
+
+ return { ok: true }
+}
+```
+
+To guard a whole route, compose `checkMiddleware` after setup:
+
+```ts title="app/routes/posts.new.tsx"
+import { permix } from '../lib/permix'
+
+export const middleware = [permix.checkMiddleware('post.create')]
+```
+
+Denied requests default to `403` with `{ error: 'Forbidden' }`. Customize with `onForbidden` (for example to `redirect`).
+
+
+
+
+
+## Hydrate the client
+
+Dehydrate in a loader (often the root loader) and wrap the tree with `permix/react`:
+
+```tsx title="app/root.tsx"
+import { PermixHydrate, PermixProvider } from 'permix/react'
+import { createPermix } from 'permix'
+import { permix as serverPermix } from './lib/permix'
+import type { PermissionsDefinition } from './lib/permix'
+import type { Route } from './+types/root'
+
+const clientPermix = createPermix()
+
+export async function loader({ context }: Route.LoaderArgs) {
+ return { permixState: serverPermix.dehydrate(context) }
+}
+
+export function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ )
+}
+
+export default function App({ loaderData }: Route.ComponentProps) {
+ return (
+
+
+
+
+
+ )
+}
+```
+
+
+ `hydrate()` restores booleans but does not restore function-based rules or
+ mark the instance ready. Call `clientPermix.setup(...)` on the client with the
+ full rule set after hydration. See the [Hydration
+ guide](/docs/guide/hydration).
+
+
+
+
+
+
+## Use on the client
+
+```tsx title="app/components/edit-button.tsx"
+import { usePermix } from 'permix/react'
+
+export function EditButton({
+ permix,
+ post,
+}: {
+ permix: Parameters[0]
+ post: { id: string; authorId: string }
+}) {
+ const { check } = usePermix(permix)
+
+ if (!check('post.update', post)) {
+ return null
+ }
+
+ return Edit post
+}
+```
+
+Pass the same client singleton you gave to `PermixProvider`. See the [React integration](/docs/integrations/react) for `createComponents`.
+
+
+
+
+
+## Templates
+
+```ts title="app/lib/permix.ts"
+import { createPermix } from 'permix/react-router'
+
+export const permix = createPermix<{
+ post: ['create', 'read', 'update', 'delete']
+}>()
+
+export const adminTemplate = permix.template({
+ post: { create: true, read: true, update: true, delete: true },
+})
+
+export const guestTemplate = permix.template({
+ post: { create: false, read: true, update: false, delete: false },
+})
+```
+
+```ts title="app/root.tsx"
+export const middleware = [
+ permix.setupMiddleware(async ({ request }) => {
+ const session = await getSession(request)
+ return session?.role === 'admin' ? adminTemplate() : guestTemplate()
+ }),
+]
+```
+
+
+
+
+
+## Example
+
+You can find a runnable example of the React Router integration [here](https://github.com/letstri/permix/tree/main/examples/react-router).
+
+
+
+
+
+## Remix
+
+Remix merged into React Router 7. Use `permix/react-router` — do not look for a `permix/remix` export.
+
+## API
+
+### `createPermix(options?)`
+
+| Method | Description |
+| --- | --- |
+| `setupMiddleware(rules \| callback)` | Middleware that creates a per-request instance on `context`. |
+| `checkMiddleware(...args)` | Middleware that allows or returns the `onForbidden` response. |
+| `get(context)` | Return the instance, or `null`. |
+| `getOrThrow(context)` | Return the instance, or throw `PermixNotFoundError`. |
+| `dehydrate(context)` | Serialize rules for ``. |
+| `getRules(context)` | Return the current rules, or `null`. |
+| `template(rules)` | Create a reusable rule set. |
+| `context` | The opaque key passed to `context.set` / `context.get`. |
diff --git a/docs/content/docs/integrations/react.mdx b/docs/content/docs/integrations/react.mdx
index 2eb3e010..681922c2 100644
--- a/docs/content/docs/integrations/react.mdx
+++ b/docs/content/docs/integrations/react.mdx
@@ -156,7 +156,7 @@ function App({ dehydratedState }: { dehydratedState: DehydratedState }) {
}
```
-See the [Hydration guide](/docs/guide/hydration) and framework-specific pages for [Next.js](/docs/integrations/next) and [TanStack Start](/docs/integrations/tanstack-start).
+See the [Hydration guide](/docs/guide/hydration) and framework-specific pages for [Next.js](/docs/integrations/next), [TanStack Start](/docs/integrations/tanstack-start), and [React Router](/docs/integrations/react-router).
diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json
index dbd79bfc..bd98edf3 100644
--- a/docs/content/docs/meta.json
+++ b/docs/content/docs/meta.json
@@ -18,6 +18,7 @@
"integrations/react",
"integrations/next",
"integrations/tanstack-start",
+ "integrations/react-router",
"integrations/vue",
"integrations/solid",
"integrations/svelte",
diff --git a/docs/content/docs/migration-v3-to-v4.mdx b/docs/content/docs/migration-v3-to-v4.mdx
index 20be84ca..b9a12166 100644
--- a/docs/content/docs/migration-v3-to-v4.mdx
+++ b/docs/content/docs/migration-v3-to-v4.mdx
@@ -240,7 +240,7 @@ permix.setup(getClientRules(user))
`hydrate()` fires the **`setup` hook** (not a separate `hydrate` hook). Update listeners that used `hook('hydrate', ...)` in v3.
-See [Hydration](/docs/guide/hydration) and [Ready state](/docs/guide/ready). For App Router / TanStack Start, see [Next.js](/docs/integrations/next) and [TanStack Start](/docs/integrations/tanstack-start).
+See [Hydration](/docs/guide/hydration) and [Ready state](/docs/guide/ready). For App Router / TanStack Start / React Router, see [Next.js](/docs/integrations/next), [TanStack Start](/docs/integrations/tanstack-start), and [React Router](/docs/integrations/react-router).
---
@@ -340,6 +340,7 @@ These are additive — migrate the core API first, then adopt what you need:
| ----------------------- | ------------------------------------------- |
| `permix/next` | Next.js App Router, request-scoped instance |
| `permix/tanstack-start` | TanStack Start middleware and SSR |
+| `permix/react-router` | React Router 7 middleware and SSR |
| `permix/server` | Framework-agnostic fetch middleware |
| `permix/svelte` | Svelte 5 runes |
| `permix/drizzle` | Rules from Drizzle v1 schema |
diff --git a/docs/content/docs/quick-start.mdx b/docs/content/docs/quick-start.mdx
index c6bc2196..ba3ba407 100644
--- a/docs/content/docs/quick-start.mdx
+++ b/docs/content/docs/quick-start.mdx
@@ -170,6 +170,10 @@ Continuing from the quick start, you can now explore how Permix integrates with
Integration with TanStack Start.
+
+ Integration with React Router 7 middleware and hydration.
+
+
Integration with Solid via provider and hook.
diff --git a/examples/react-router/main.ts b/examples/react-router/main.ts
new file mode 100644
index 00000000..e22135d0
--- /dev/null
+++ b/examples/react-router/main.ts
@@ -0,0 +1,59 @@
+import { createServer } from 'node:http'
+
+import type { ValidateDefinition } from 'permix'
+import { createPermix } from 'permix/react-router'
+import type { ReactRouterContext } from 'permix/react-router'
+
+type PermissionsDefinition = ValidateDefinition<{
+ user: ['read', 'write']
+}>
+
+const permix = createPermix({
+ onForbidden: () =>
+ Response.json(
+ { error: 'You do not have permission to access this resource' },
+ { status: 403 }
+ ),
+})
+
+function createContext(): ReactRouterContext {
+ const store = new Map()
+ return {
+ get: (key) => store.get(key),
+ set: (key, value) => {
+ store.set(key, value)
+ },
+ }
+}
+
+async function handle(request: Request): Promise {
+ const context = createContext()
+
+ return permix.setupMiddleware({
+ user: {
+ read: true,
+ write: false,
+ },
+ })({ request, context }, async () => {
+ const url = new URL(request.url)
+
+ if (url.pathname === '/write') {
+ return permix.checkMiddleware('user.write')({ request, context }, () =>
+ Response.json({ ok: true })
+ )
+ }
+
+ return Response.json({
+ canRead: permix.getOrThrow(context).check('user.read'),
+ })
+ })
+}
+
+createServer(async (req, res) => {
+ const request = new Request(`http://127.0.0.1:3000${req.url ?? '/'}`)
+ const response = await handle(request)
+ res.writeHead(response.status, Object.fromEntries(response.headers))
+ res.end(Buffer.from(await response.arrayBuffer()))
+}).listen(3000, () => {
+ console.log('Server is running on port 3000')
+})
diff --git a/examples/react-router/package.json b/examples/react-router/package.json
new file mode 100644
index 00000000..5dea0633
--- /dev/null
+++ b/examples/react-router/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "react-router",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "check-types": "tsc --noEmit",
+ "start": "tsx main.ts"
+ },
+ "dependencies": {
+ "permix": "workspace:*"
+ },
+ "devDependencies": {
+ "@types/node": "^25.9.1",
+ "tsx": "^4.22.4"
+ }
+}
diff --git a/examples/react-router/tsconfig.json b/examples/react-router/tsconfig.json
new file mode 100644
index 00000000..48ce3c61
--- /dev/null
+++ b/examples/react-router/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "strict": true,
+ "types": ["node"],
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ }
+}
diff --git a/permix/package.json b/permix/package.json
index dfbefd33..f2e5af2d 100644
--- a/permix/package.json
+++ b/permix/package.json
@@ -15,6 +15,7 @@
"permissions-management",
"rbac",
"react",
+ "react-router",
"security",
"solid",
"svelte",
@@ -116,6 +117,10 @@
"./tanstack-start": {
"types": "./dist/tanstack-start/index.d.mts",
"import": "./dist/tanstack-start/index.mjs"
+ },
+ "./react-router": {
+ "types": "./dist/react-router/index.d.mts",
+ "import": "./dist/react-router/index.mjs"
}
},
"scripts": {
diff --git a/permix/skills/permix/SKILL.md b/permix/skills/permix/SKILL.md
index b3ebc1b5..dd831f20 100644
--- a/permix/skills/permix/SKILL.md
+++ b/permix/skills/permix/SKILL.md
@@ -1,7 +1,7 @@
---
name: permix
description: >-
- Applies Permix authorization once a schema exists: permix.check() paths and ReBAC callbacks, frontend bindings (permix/react, permix/vue, permix/solid, permix/svelte) with SSR dehydrate/hydrate for Next.js and TanStack Start, and server middleware (permix/express, hono, fastify, trpc, orpc, node, elysia). Use for anything past initial setup — checking permissions, gating UI, or protecting routes. For creating the schema and first `permix.setup()`, use permix-getting-started first.
+ Applies Permix authorization once a schema exists: permix.check() paths and ReBAC callbacks, frontend bindings (permix/react, permix/vue, permix/solid, permix/svelte) with SSR dehydrate/hydrate for Next.js, TanStack Start, and React Router, and server middleware (permix/express, hono, fastify, trpc, orpc, node, elysia). Use for anything past initial setup — checking permissions, gating UI, or protecting routes. For creating the schema and first `permix.setup()`, use permix-getting-started first.
metadata:
type: core
library: permix
@@ -19,6 +19,7 @@ sources:
- 'letstri/permix:docs/content/docs/integrations/svelte.mdx'
- 'letstri/permix:docs/content/docs/integrations/next.mdx'
- 'letstri/permix:docs/content/docs/integrations/tanstack-start.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/react-router.mdx'
- 'letstri/permix:docs/content/docs/integrations/express.mdx'
- 'letstri/permix:docs/content/docs/integrations/hono.mdx'
- 'letstri/permix:docs/content/docs/integrations/fastify.mdx'
@@ -37,7 +38,7 @@ Assumes a `permix` instance already exists (see **permix-getting-started**). Loa
| Task | Reference |
| --- | --- |
| `permix.check()` paths, callbacks, `~all`/`~any`, ReBAC/ABAC with entity data, `isReady` | [references/check.md](references/check.md) |
-| React, Vue, Solid, or Svelte UI — `PermixProvider`, `usePermix`, `createComponents`, SSR `dehydrate`/`hydrate` for Next.js / TanStack Start | [references/frontend.md](references/frontend.md) |
+| React, Vue, Solid, or Svelte UI — `PermixProvider`, `usePermix`, `createComponents`, SSR `dehydrate`/`hydrate` for Next.js / TanStack Start / React Router | [references/frontend.md](references/frontend.md) |
| Protecting Express, Hono, Fastify, tRPC, oRPC, Node, or Elysia routes — `setupMiddleware`, `checkMiddleware` | [references/server.md](references/server.md) |
## Rules that apply everywhere
diff --git a/permix/skills/permix/references/frontend.md b/permix/skills/permix/references/frontend.md
index 510f619c..14b4f62b 100644
--- a/permix/skills/permix/references/frontend.md
+++ b/permix/skills/permix/references/frontend.md
@@ -188,9 +188,9 @@ function App({
Run client `permix.setup(...)` where you restore the session (e.g. after `PermixHydrate` mounts or in the same auth effect).
-### Next.js / TanStack Start
+### Next.js / TanStack Start / React Router
-Use framework helpers from `permix/next` or `permix/tanstack-start` when available — they wire dehydrate/hydrate into the framework data flow.
+Use framework helpers from `permix/next`, `permix/tanstack-start`, or `permix/react-router` when available — they wire dehydrate/hydrate into the framework data flow. React Router 7 covers Remix; there is no `permix/remix` export.
In TanStack Start, `permix.get(context)` only works in server functions and server routes. To check inside `beforeLoad`/`loader`, put a core instance on the **router context** in `getRouter()` (`context: { permix }`), type it with `createRootRouteWithContext`, hydrate it in the root route's `beforeLoad`, then call `context.permix.check(...)` in any child route. Passing only the context type without the runtime value leaves `context.permix` undefined.
@@ -200,6 +200,7 @@ Docs:
- https://permix.letstri.dev/docs/integrations/next
- https://permix.letstri.dev/docs/integrations/tanstack-start
+- https://permix.letstri.dev/docs/integrations/react-router
### Flow diagram
@@ -225,5 +226,6 @@ For static-only permissions (all booleans), dehydrate + hydrate + `setup` with t
- Solid: https://github.com/letstri/permix/tree/main/examples/solid
- Svelte: https://github.com/letstri/permix/tree/main/examples/svelte
- Next.js (SSR): https://github.com/letstri/permix/tree/main/examples/next
+- React Router 7 (SSR): https://github.com/letstri/permix/tree/main/examples/react-router
- Role templates: https://github.com/letstri/permix/tree/main/examples/role-based
- ReBAC: https://github.com/letstri/permix/tree/main/examples/rebac
diff --git a/permix/src/react-router/index.ts b/permix/src/react-router/index.ts
new file mode 100644
index 00000000..60dafabb
--- /dev/null
+++ b/permix/src/react-router/index.ts
@@ -0,0 +1 @@
+export * from './permix'
diff --git a/permix/src/react-router/permix.test.ts b/permix/src/react-router/permix.test.ts
new file mode 100644
index 00000000..03c4eaed
--- /dev/null
+++ b/permix/src/react-router/permix.test.ts
@@ -0,0 +1,331 @@
+import { describe, expect, it, vi } from 'vitest'
+
+import type { ValidateDefinition } from '../core'
+import { PermixNotFoundError } from '../core'
+import { createPermix } from './permix'
+import type { ReactRouterContext } from './permix'
+
+interface Post {
+ id: string
+ authorId: string
+}
+
+type PermissionsDefinition = ValidateDefinition<{
+ post: ['create', 'read', 'update']
+ user: ['delete']
+}>
+
+type PostWithData = ValidateDefinition<{
+ post: [{ name: 'create'; type: Post }]
+}>
+
+function createMockContext(): ReactRouterContext {
+ const store = new Map()
+ return {
+ get: (key) => store.get(key),
+ set: (key, value) => {
+ store.set(key, value)
+ },
+ }
+}
+
+function createMockNext(response = new Response('ok')) {
+ return vi.fn(async () => response)
+}
+
+describe(createPermix, () => {
+ const permix = createPermix()
+
+ it('should throw ts error', () => {
+ // @ts-expect-error path does not exist
+ permix.checkMiddleware('post.delete')
+ })
+
+ it('should allow access when permission is granted', async () => {
+ const context = createMockContext()
+ const next = createMockNext()
+ const request = new Request('https://example.com')
+
+ await permix.setupMiddleware({
+ post: { create: true, read: false, update: false },
+ user: { delete: false },
+ })({ request, context }, next)
+
+ const result = await permix.checkMiddleware('post.create')(
+ { request, context },
+ next
+ )
+
+ expect(result?.status).toBe(200)
+ expect(next).toHaveBeenCalledTimes(2)
+ })
+
+ it('should deny access when permission is not granted', async () => {
+ const context = createMockContext()
+ const next = createMockNext()
+ const request = new Request('https://example.com')
+
+ await permix.setupMiddleware({
+ post: { create: false, read: false, update: false },
+ user: { delete: false },
+ })({ request, context }, next)
+
+ const result = await permix.checkMiddleware('post.create')(
+ { request, context },
+ next
+ )
+
+ expect(result?.status).toBe(403)
+ await expect(result?.text()).resolves.toBe(
+ JSON.stringify({ error: 'Forbidden' })
+ )
+ expect(next).toHaveBeenCalledOnce()
+ })
+
+ it('should work with custom error handler', async () => {
+ const permix = createPermix({
+ onForbidden: () =>
+ new Response(JSON.stringify({ error: 'Custom error' }), {
+ status: 403,
+ headers: { 'Content-Type': 'application/json' },
+ }),
+ })
+
+ const context = createMockContext()
+ const next = createMockNext()
+ const request = new Request('https://example.com')
+
+ await permix.setupMiddleware({
+ post: { create: false, read: false, update: false },
+ user: { delete: false },
+ })({ request, context }, next)
+
+ const result = await permix.checkMiddleware('post.create')(
+ { request, context },
+ next
+ )
+
+ expect(result?.status).toBe(403)
+ await expect(result?.text()).resolves.toBe(
+ JSON.stringify({ error: 'Custom error' })
+ )
+ })
+
+ it('should pass data through to a rule callback', async () => {
+ const permix = createPermix()
+ const context = createMockContext()
+ const next = createMockNext()
+ const request = new Request('https://example.com')
+
+ await permix.setupMiddleware({
+ post: {
+ create: (post) => post?.authorId === '1',
+ },
+ })({ request, context }, next)
+
+ const result = await permix.checkMiddleware('post.create', {
+ id: 'a',
+ authorId: '1',
+ })({ request, context }, next)
+
+ expect(result?.status).toBe(200)
+ })
+
+ it('should work with checker callback form', async () => {
+ const permix = createPermix()
+ const context = createMockContext()
+ const next = createMockNext()
+ const request = new Request('https://example.com')
+
+ await permix.setupMiddleware({
+ post: { create: true, read: true, update: false },
+ user: { delete: true },
+ })({ request, context }, next)
+
+ const result = await permix.checkMiddleware(
+ (c) => c('post.create') && c('user.delete')
+ )({ request, context }, next)
+
+ expect(result?.status).toBe(200)
+ })
+
+ it('should work with an async setup callback that receives the request', async () => {
+ const permix = createPermix()
+ const context = createMockContext()
+ const next = createMockNext()
+ const request = new Request('https://example.com/?admin=1')
+
+ await permix.setupMiddleware(async ({ request: req }) => ({
+ post: {
+ create: new URL(req.url).searchParams.get('admin') === '1',
+ read: true,
+ update: false,
+ },
+ user: { delete: false },
+ }))({ request, context }, next)
+
+ expect(permix.getOrThrow(context).check('post.create')).toBe(true)
+ })
+
+ it('should dehydrate permissions', async () => {
+ const permix = createPermix()
+ const context = createMockContext()
+ const next = createMockNext()
+ const request = new Request('https://example.com')
+
+ await permix.setupMiddleware({
+ post: { create: true, read: false, update: true },
+ user: { delete: false },
+ })({ request, context }, next)
+
+ expect(permix.dehydrate(context)).toStrictEqual({
+ post: { create: true, read: false, update: true },
+ user: { delete: false },
+ })
+ })
+
+ it('should isolate instances between requests', async () => {
+ const permix = createPermix()
+ const next = createMockNext()
+
+ const admin = createMockContext()
+ await permix.setupMiddleware({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ })(
+ { request: new Request('https://example.com/admin'), context: admin },
+ next
+ )
+
+ const guest = createMockContext()
+ await permix.setupMiddleware({
+ post: { create: false, read: true, update: false },
+ user: { delete: false },
+ })({ request: new Request('https://example.com'), context: guest }, next)
+
+ expect(permix.getOrThrow(admin).check('post.create')).toBe(true)
+ expect(permix.getOrThrow(guest).check('post.create')).toBe(false)
+ })
+
+ it('should isolate independent factories on the same request context', async () => {
+ const first = createPermix()
+ const second = createPermix()
+ const context = createMockContext()
+ const next = createMockNext()
+ const request = new Request('https://example.com')
+
+ await first.setupMiddleware({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ })({ request, context }, next)
+ await second.setupMiddleware({
+ post: { create: false, read: false, update: false },
+ user: { delete: false },
+ })({ request, context }, next)
+
+ expect(first.getOrThrow(context).check('post.create')).toBe(true)
+ expect(second.getOrThrow(context).check('post.create')).toBe(false)
+ })
+
+ it('should work with template', async () => {
+ const permix = createPermix()
+ const template = permix.template({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ })
+
+ const context = createMockContext()
+ const next = createMockNext()
+ const request = new Request('https://example.com')
+
+ await permix.setupMiddleware(() => template())({ request, context }, next)
+
+ const result = await permix.checkMiddleware('post.create')(
+ { request, context },
+ next
+ )
+
+ expect(result?.status).toBe(200)
+ })
+})
+
+describe('get / getOrThrow', () => {
+ const permix = createPermix()
+
+ it('should return null when setupMiddleware has not run', () => {
+ expect(permix.get(createMockContext())).toBeNull()
+ })
+
+ it('should return the instance when setupMiddleware has run', async () => {
+ const context = createMockContext()
+ const next = createMockNext()
+
+ await permix.setupMiddleware({
+ post: { create: true, read: true, update: true },
+ user: { delete: true },
+ })({ request: new Request('https://example.com'), context }, next)
+
+ expect(permix.getOrThrow(context).check).toBeTypeOf('function')
+ })
+
+ it('getOrThrow should throw PermixNotFoundError when missing', () => {
+ expect(() => permix.getOrThrow(createMockContext())).toThrow(
+ PermixNotFoundError
+ )
+ })
+})
+
+describe('checkMiddleware without setupMiddleware', () => {
+ it('should throw PermixNotFoundError', async () => {
+ const permix = createPermix()
+ const next = createMockNext()
+
+ await expect(
+ permix.checkMiddleware('post.create')(
+ {
+ request: new Request('https://example.com'),
+ context: createMockContext(),
+ },
+ next
+ )
+ ).rejects.toBeInstanceOf(PermixNotFoundError)
+ expect(next).not.toHaveBeenCalled()
+ })
+})
+
+describe('React Router-style middleware composition', () => {
+ it('should compose setup and check middleware', async () => {
+ const permix = createPermix()
+ const context = createMockContext()
+ const request = new Request('https://example.com')
+
+ const middleware = [
+ permix.setupMiddleware({
+ post: { create: true, read: false, update: false },
+ user: { delete: false },
+ }),
+ permix.checkMiddleware('post.create'),
+ ]
+
+ let index = 0
+ const dispatch = async (): Promise => {
+ const handler = middleware[index++]
+ if (handler) {
+ return await handler({ request, context }, dispatch)
+ }
+ return Response.json({ ok: true })
+ }
+
+ const res = await dispatch()
+ expect(res.status).toBe(200)
+ await expect(res.json()).resolves.toStrictEqual({ ok: true })
+ })
+})
+
+describe('context key', () => {
+ it('should expose a unique context key per factory', () => {
+ const first = createPermix()
+ const second = createPermix()
+ expect(first.context).not.toBe(second.context)
+ })
+})
diff --git a/permix/src/react-router/permix.ts b/permix/src/react-router/permix.ts
new file mode 100644
index 00000000..02a30740
--- /dev/null
+++ b/permix/src/react-router/permix.ts
@@ -0,0 +1,216 @@
+import type { Permix as PermixCore } from '../core'
+import {
+ createCheckContext,
+ createHooks,
+ createPermix as createPermixCore,
+ createTemplate,
+ PermixNotFoundError,
+} from '../core'
+import type { CheckArgs, CheckContext } from '../core/check'
+import type { Definition } from '../core/definitions'
+import type { PermixHooks, Rules, RulesPaths } from '../core/permix'
+import type { DehydratedState } from '../core/rules'
+import type { MaybePromise } from '../utils'
+
+/**
+ * Opaque key used with React Router's `context.set` / `context.get`.
+ * Compatible with `RouterContext` from `react-router`.
+ */
+export interface ReactRouterContextKey {
+ readonly __permix?: T
+}
+
+/**
+ * Structural React Router middleware context. Compatible with
+ * `RouterContextProvider` from `react-router`.
+ */
+export interface ReactRouterContext {
+ get: (key: ReactRouterContextKey) => unknown
+ set: (key: ReactRouterContextKey, value: unknown) => void
+}
+
+export interface MiddlewareArgs {
+ request: Request
+ context: ReactRouterContext
+ params?: Record
+}
+
+export type MiddlewareNext = () => MaybePromise
+
+/**
+ * React Router middleware: `(args, next) => Response`. Compatible with
+ * `MiddlewareFunction` from `react-router` 7.9+.
+ */
+export type ReactRouterMiddleware = (
+ args: MiddlewareArgs,
+ next: MiddlewareNext
+) => MaybePromise
+
+export interface SetupContext {
+ request: Request
+ params?: Record
+}
+
+export interface MiddlewareContext {
+ request: Request
+ context: ReactRouterContext
+ next: MiddlewareNext
+}
+
+export interface PermixOptions {
+ /**
+ * Called when a `checkMiddleware` denies the request. Defaults to a 403 JSON
+ * response of `{ error: 'Forbidden' }`.
+ */
+ onForbidden?: (
+ params: CheckContext & MiddlewareContext
+ ) => MaybePromise
+}
+
+function buildPermix(
+ resolveKey: () => ReactRouterContextKey>,
+ options: PermixOptions = {}
+) {
+ const onForbidden =
+ options.onForbidden ??
+ (() =>
+ new Response(JSON.stringify({ error: 'Forbidden' }), {
+ status: 403,
+ headers: { 'Content-Type': 'application/json' },
+ }))
+
+ const hooks = createHooks>()
+
+ function get(
+ context: ReactRouterContext | null | undefined
+ ): PermixCore | null {
+ const instance = context?.get(resolveKey()) as PermixCore | undefined
+ return instance ?? null
+ }
+
+ function getOrThrow(
+ context: ReactRouterContext | null | undefined
+ ): PermixCore {
+ const instance = get(context)
+ if (!instance) {
+ throw new PermixNotFoundError()
+ }
+ return instance
+ }
+
+ function setupMiddleware(
+ callbackOrRules:
+ | ((setup: SetupContext) => MaybePromise>)
+ | Rules
+ ): ReactRouterMiddleware {
+ return async ({ request, context, params }, next) => {
+ const rules =
+ typeof callbackOrRules === 'function'
+ ? await callbackOrRules({ request, params })
+ : callbackOrRules
+ const instance = createPermixCore(rules)
+ instance.hook('check', (checkContext) => {
+ hooks.callHook('check', checkContext)
+ })
+ context.set(resolveKey(), instance)
+ return await next()
+ }
+ }
+
+ const checkMiddleware: (...args: CheckArgs) => ReactRouterMiddleware =
+ (...args) =>
+ async ({ request, context }, next) => {
+ const permix = get(context)
+
+ if (!permix) {
+ throw new PermixNotFoundError()
+ }
+
+ const allowed = permix.check(...args)
+
+ if (!allowed) {
+ return await onForbidden({
+ request,
+ context,
+ next,
+ ...createCheckContext(...args),
+ })
+ }
+
+ return await next()
+ }
+
+ function dehydrate(
+ context: ReactRouterContext | null | undefined
+ ): DehydratedState {
+ return getOrThrow(context).dehydrate()
+ }
+
+ function getRules(
+ context: ReactRouterContext | null | undefined
+ ): Rules | null {
+ return get(context)?.getRules() ?? null
+ }
+
+ function template(rules: Rules | ((param: T) => Rules)) {
+ return createTemplate(rules)
+ }
+
+ return {
+ setupMiddleware,
+ checkMiddleware,
+ get,
+ getOrThrow,
+ dehydrate,
+ getRules,
+ template,
+ hook: hooks.hook,
+ hookOnce: hooks.hookOnce,
+ get context() {
+ return resolveKey()
+ },
+ $inferDefinition: undefined as unknown as D,
+ $inferPath: undefined as unknown as RulesPaths,
+ }
+}
+
+/**
+ * Create a per-request Permix helper for React Router 7 (including Remix).
+ *
+ * Uses React Router middleware context (`context.set` / `context.get`) so
+ * loaders, actions, and middleware share one instance per request. Hydrate
+ * the client with `permix/react`.
+ *
+ * @example
+ * ```ts
+ * // app/lib/permix.ts
+ * import { createPermix } from 'permix/react-router'
+ *
+ * export const permix = createPermix<{
+ * post: ['create', 'read', 'update', 'delete']
+ * }>()
+ * ```
+ *
+ * ```ts
+ * // app/root.tsx
+ * import { permix } from './lib/permix'
+ *
+ * export const middleware = [
+ * permix.setupMiddleware(({ request }) => ({
+ * post: { create: true, read: true, update: false, delete: false },
+ * })),
+ * ]
+ * ```
+ *
+ * @link https://permix.letstri.dev/docs/integrations/react-router
+ */
+export function createPermix(
+ options: PermixOptions = {}
+) {
+ const key: ReactRouterContextKey> = {}
+ return buildPermix(() => key, options)
+}
+
+export type ReactRouterPermix = ReturnType<
+ typeof createPermix
+>
diff --git a/permix/tsdown.config.ts b/permix/tsdown.config.ts
index 00e9a0a3..3463d426 100644
--- a/permix/tsdown.config.ts
+++ b/permix/tsdown.config.ts
@@ -22,6 +22,7 @@ export default defineConfig({
'./src/drizzle/legacy/index.ts',
'./src/next/index.ts',
'./src/tanstack-start/index.ts',
+ './src/react-router/index.ts',
],
dts: {
build: true,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 761d130a..c80189cf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -325,6 +325,19 @@ importers:
specifier: ^8.0.16
version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)
+ examples/react-router:
+ dependencies:
+ permix:
+ specifier: workspace:*
+ version: link:../../permix
+ devDependencies:
+ '@types/node':
+ specifier: ^25.9.1
+ version: 25.9.1
+ tsx:
+ specifier: ^4.22.4
+ version: 4.22.4
+
examples/rebac:
dependencies:
permix:
From 3752c952998d3ca45234bbcfa3c635086cc6da61 Mon Sep 17 00:00:00 2001
From: Martijn van Dijk
Date: Fri, 28 Aug 2026 15:32:39 +0200
Subject: [PATCH 15/28] feat: infer entity types from Zod and other Standard
Schema validators
Reuse existing Zod, Valibot, or ArkType schemas on action specs and via
permix/standard-schema so ReBAC callbacks stay typed without duplicate
interfaces. check() still does not parse data at runtime.
Co-authored-by: Cursor
---
_artifacts/domain_map.yaml | 1 +
_artifacts/skill_spec.md | 2 +-
_artifacts/skill_tree.yaml | 1 +
docs/content/docs/comparison.mdx | 1 +
docs/content/docs/guide/instance.mdx | 59 +++-
docs/content/docs/guide/setup.mdx | 24 ++
.../docs/integrations/standard-schema.mdx | 208 ++++++++++++++
docs/content/docs/meta.json | 1 +
docs/content/docs/migration-v3-to-v4.mdx | 19 +-
docs/content/docs/quick-start.mdx | 4 +
docs/package.json | 3 +-
permix/package.json | 5 +
permix/skills/README.md | 1 +
permix/skills/permix-getting-started/SKILL.md | 18 ++
permix/skills/permix/references/check.md | 2 +
permix/skills/permix/references/server.md | 3 +-
permix/src/core/definitions.ts | 24 ++
permix/src/core/index.ts | 1 +
permix/src/core/permix.ts | 22 +-
permix/src/core/rules.ts | 12 +-
permix/src/core/standard-schema.test.ts | 149 ++++++++++
permix/src/core/standard-schema.ts | 103 +++++++
permix/src/standard-schema/errors.ts | 20 ++
permix/src/standard-schema/index.ts | 2 +
permix/src/standard-schema/permix.test.ts | 152 ++++++++++
permix/src/standard-schema/permix.ts | 266 ++++++++++++++++++
permix/tsdown.config.ts | 1 +
pnpm-lock.yaml | 104 ++++---
28 files changed, 1127 insertions(+), 81 deletions(-)
create mode 100644 docs/content/docs/integrations/standard-schema.mdx
create mode 100644 permix/src/core/standard-schema.test.ts
create mode 100644 permix/src/core/standard-schema.ts
create mode 100644 permix/src/standard-schema/errors.ts
create mode 100644 permix/src/standard-schema/index.ts
create mode 100644 permix/src/standard-schema/permix.test.ts
create mode 100644 permix/src/standard-schema/permix.ts
diff --git a/_artifacts/domain_map.yaml b/_artifacts/domain_map.yaml
index 318aa7c9..daba3755 100644
--- a/_artifacts/domain_map.yaml
+++ b/_artifacts/domain_map.yaml
@@ -68,6 +68,7 @@ skills:
- 'letstri/permix:docs/content/docs/guide/instance.mdx'
- 'letstri/permix:docs/content/docs/guide/events.mdx'
- 'letstri/permix:docs/content/docs/migration-v3-to-v4.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/standard-schema.mdx'
- 'letstri/permix:permix/src/core/index.ts'
- name: 'Permix (check, frontend, server)'
diff --git a/_artifacts/skill_spec.md b/_artifacts/skill_spec.md
index 143b1571..4473e4dc 100644
--- a/_artifacts/skill_spec.md
+++ b/_artifacts/skill_spec.md
@@ -47,7 +47,7 @@ When `docs/content/docs/` or public API in `permix/src/` changes:
## Out of scope (docs-only for now)
-- `permix/effect`, `permix/drizzle` — documented at https://permix.letstri.dev/docs/integrations/effect and `/drizzle`; no dedicated skill yet.
+- `permix/effect`, `permix/drizzle`, `permix/standard-schema` — documented at https://permix.letstri.dev/docs/integrations/effect, `/drizzle`, and `/standard-schema`; no dedicated skill yet.
## Registry
diff --git a/_artifacts/skill_tree.yaml b/_artifacts/skill_tree.yaml
index 70b42866..a2357f8d 100644
--- a/_artifacts/skill_tree.yaml
+++ b/_artifacts/skill_tree.yaml
@@ -27,6 +27,7 @@ skills:
- 'letstri/permix:docs/content/docs/guide/instance.mdx'
- 'letstri/permix:docs/content/docs/guide/events.mdx'
- 'letstri/permix:docs/content/docs/migration-v3-to-v4.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/standard-schema.mdx'
- 'letstri/permix:permix/src/core/index.ts'
- name: 'Permix (check, frontend, server)'
diff --git a/docs/content/docs/comparison.mdx b/docs/content/docs/comparison.mdx
index d0d9ea67..fc8e4a7b 100644
--- a/docs/content/docs/comparison.mdx
+++ b/docs/content/docs/comparison.mdx
@@ -51,6 +51,7 @@ Built with `pnpm run build` in the `permix` package (`tsdown` for all entries ex
| `permix/effect` | 1.28 kB | adapter |
| `permix/drizzle` | 0.89 kB | adapter |
| `permix/drizzle/legacy` | 0.83 kB | adapter |
+| `permix/standard-schema` | 1.46 kB | adapter |
Integration entries import `../core/index.mjs`, so a typical app ships **core + adapter** (for example React ≈ **2.64 + 0.86 ≈ 3.50 kB** gzip of published chunks before your bundler minifies further).
diff --git a/docs/content/docs/guide/instance.mdx b/docs/content/docs/guide/instance.mdx
index 8b682504..3b9c8964 100644
--- a/docs/content/docs/guide/instance.mdx
+++ b/docs/content/docs/guide/instance.mdx
@@ -73,11 +73,68 @@ const canEdit = permix.check('post.edit') // false
const canEditWithPost = permix.check('post.edit', somePost) // true
```
+#### `schema`
+
+
+ Not required. Alternative to `type` when you already have a Zod (or Valibot,
+ ArkType, …) schema.
+
+
+Pass a [Standard Schema](https://standardschema.dev/) on the action spec. Entity data is inferred from the schema's output type, so you do not need a separate `interface Post`. If both `type` and `schema` are set, `type` wins.
+
+```ts twoslash title="/lib/permix-schema.ts"
+import { action, createPermix } from 'permix'
+import { z } from 'zod'
+
+const postSchema = z.object({
+ id: z.string(),
+ author: z.string(),
+})
+
+const permix = createPermix<{
+ post: [{ name: 'edit'; schema: typeof postSchema }]
+}>()
+
+permix.setup({
+ post: {
+ edit: (post) => post?.author === 'John Doe',
+ // ^?
+ },
+})
+```
+
+`action(name, schema, { required }?)` builds the same spec from a value. Use `as const` on the definition so action names stay literal:
+
+```ts twoslash title="/lib/permix-action.ts"
+import { action, createPermix } from 'permix'
+import { z } from 'zod'
+
+const postSchema = z.object({
+ id: z.string(),
+ authorId: z.string(),
+})
+
+const definition = {
+ post: ['create', action('edit', postSchema, { required: true })],
+} as const
+
+const permix = createPermix()
+
+permix.setup({
+ post: {
+ create: true,
+ edit: (post) => post.authorId === 'user-1',
+ },
+})
+```
+
+To generate a whole CRUD tree from a map of schemas, see the [Standard Schema](/docs/integrations/standard-schema) integration.
+
#### `required`
Not required, defaults to `false`.
-By default, when an action declares a `type`, the data argument in `check` is optional. Set `required: true` to require data for that action.
+By default, when an action declares a `type` (or `schema`), the data argument in `check` is optional. Set `required: true` to require data for that action.
```ts twoslash title="/lib/permix-type-required.ts"
import { createPermix } from 'permix'
diff --git a/docs/content/docs/guide/setup.mdx b/docs/content/docs/guide/setup.mdx
index 4b95750d..22986a41 100644
--- a/docs/content/docs/guide/setup.mdx
+++ b/docs/content/docs/guide/setup.mdx
@@ -105,6 +105,30 @@ permix.setup({
})
```
+### Standard Schema
+
+If the entity already has a Zod (or Valibot, ArkType, …) schema, pass `schema` instead of `type`. See [Standard Schema](/docs/integrations/standard-schema) and [`action()`](/docs/guide/instance#schema).
+
+```ts twoslash
+import { createPermix } from 'permix'
+import { z } from 'zod'
+
+const postSchema = z.object({
+ id: z.string(),
+ authorId: z.string(),
+})
+
+const permix = createPermix<{
+ post: [{ name: 'update'; schema: typeof postSchema }]
+}>()
+
+permix.setup({
+ post: {
+ update: (post) => post?.authorId === 'user-1',
+ },
+})
+```
+
### Required
By default, a `type` on an action makes the data argument optional in `check`. Set `required: true` to require it.
diff --git a/docs/content/docs/integrations/standard-schema.mdx b/docs/content/docs/integrations/standard-schema.mdx
new file mode 100644
index 00000000..9960e98f
--- /dev/null
+++ b/docs/content/docs/integrations/standard-schema.mdx
@@ -0,0 +1,208 @@
+---
+title: Standard Schema
+description: Infer entity types from Zod, Valibot, ArkType, and other Standard Schema validators
+---
+
+## Overview
+
+Permix can take entity types from any library that implements [Standard Schema](https://standardschema.dev/) — Zod, Valibot, ArkType, Effect Schema, and others. You already have those schemas for API validation; reuse them so permission callbacks and `check()` stay aligned with the same object shape.
+
+Two entry points:
+
+| Import | Use when |
+| --- | --- |
+| `action` from `permix` | You already have a `createPermix()` tree and want a few actions typed from a schema |
+| `createPermix` from `permix/standard-schema` | You want a Drizzle-style factory: one entity per schema, CRUD actions by default |
+
+Schemas are used **only for TypeScript inference**. `check()` does not parse or validate data at runtime. Keep request-body validation in tRPC, oRPC, or your HTTP layer.
+
+
+ Before getting started, complete the initial setup steps in the [Quick
+ Start](/docs/quick-start) guide.
+
+
+
+
+
+
+## `schema` on an action spec
+
+Pass `schema: typeof yourSchema` instead of (or in addition to) `type`. Entity data is inferred from the schema's output type. If both `type` and `schema` are set, **`type` wins**.
+
+```ts twoslash title="/lib/permix-schema.ts"
+import { createPermix } from 'permix'
+import { z } from 'zod'
+
+const postSchema = z.object({
+ id: z.string(),
+ authorId: z.string(),
+})
+
+const permix = createPermix<{
+ post: ['create', { name: 'edit'; schema: typeof postSchema; required: true }]
+}>()
+
+permix.setup({
+ post: {
+ create: true,
+ edit: (post) => post.authorId === 'user-1',
+ // ^?
+ },
+})
+
+permix.check('post.edit', { id: '1', authorId: 'user-1' })
+```
+
+The same `schema` field works with Valibot, ArkType, or any other Standard Schema implementation — not only Zod.
+
+
+
+
+
+## `action()` helper
+
+`action(name, schema, { required }?)` builds an action spec so you can infer the definition from a value instead of repeating `typeof schema` on every leaf.
+
+```ts twoslash title="/lib/permix-action.ts"
+import { action, createPermix } from 'permix'
+import { z } from 'zod'
+
+const postSchema = z.object({
+ id: z.string(),
+ authorId: z.string(),
+})
+
+const definition = {
+ post: ['create', action('edit', postSchema, { required: true })],
+} as const
+
+const permix = createPermix()
+
+permix.setup({
+ post: {
+ create: true,
+ edit: (post) => post.authorId === 'user-1',
+ },
+})
+
+permix.check('post.edit', { id: '1', authorId: 'user-1' })
+```
+
+Use `as const` so action names stay literal. Nested trees work the same way — `action()` is only a leaf constructor.
+
+
+
+
+
+## Schema map factory
+
+`permix/standard-schema` turns a map of schemas into a Permix instance. Each key becomes an entity; by default each entity gets `create`, `read`, `update`, and `delete`, with entity data typed from that schema.
+
+```ts twoslash title="/lib/standard-schema.ts"
+import { createPermix } from 'permix/standard-schema'
+import { z } from 'zod'
+
+const postSchema = z.object({
+ id: z.string(),
+ authorId: z.string(),
+})
+
+const commentSchema = z.object({
+ id: z.string(),
+ postId: z.string(),
+})
+
+const permix = createPermix({
+ post: postSchema,
+ comment: commentSchema,
+})
+
+permix.setup({
+ post: {
+ create: true,
+ read: true,
+ update: (post) => post.authorId === 'user-1',
+ delete: false,
+ },
+ comment: { create: true, read: true, update: false, delete: false },
+})
+
+permix.check('post.update', { id: '1', authorId: 'user-1' })
+permix.check('comment.delete')
+```
+
+The returned instance is a regular Permix object — `check`, `setup`, `template`, `dehydrate`, `hydrate`, `hook`, `isReady` work unchanged.
+
+
+
+
+
+## Customising actions
+
+Override the action list for every bare schema, or per entity with `entity()`. Plain action tuples stay untyped (same as `createPermix<{ dashboard: ['view'] }>()`).
+
+```ts twoslash title="/lib/standard-schema-entity.ts"
+import { createPermix, entity } from 'permix/standard-schema'
+import { z } from 'zod'
+
+const postSchema = z.object({
+ id: z.string(),
+ authorId: z.string(),
+})
+
+const permix = createPermix({
+ post: entity(postSchema, [
+ 'create',
+ 'read',
+ { name: 'publish', required: true },
+ ]),
+ dashboard: ['view'],
+})
+
+permix.setup({
+ post: {
+ create: true,
+ read: true,
+ publish: (post) => post.authorId === 'user-1',
+ },
+ dashboard: { view: true },
+})
+
+permix.check('post.publish', { id: '1', authorId: 'user-1' })
+permix.check('dashboard.view')
+```
+
+Same action names for every schema, without `entity()`:
+
+```ts
+const permix = createPermix(
+ { post: postSchema, comment: commentSchema },
+ { actions: ['view', 'edit'] as const }
+)
+```
+
+
+
+
+
+## Discovering entities and actions at runtime
+
+```ts
+const permix = createPermix({ post: postSchema })
+
+permix.entities // ['post']
+permix.actions // readonly ['create', 'read', 'update', 'delete']
+```
+
+`actions` is the default set applied to **bare** schemas. `entity()` entries keep their own list.
+
+
+
+
+
+## Notes
+
+- Nested permission trees (`workspace.member`) stay on core `createPermix()` plus `action()` — the factory is one entity level, like [Drizzle](/docs/integrations/drizzle).
+- `check()` does **not** run `schema.parse`. Invalid extra fields are ignored unless your rule reads them; malformed required fields still fail inside the rule (or throw, which `~any` / `~all` / `dehydrate` treat as `false`).
+- No extra peer dependency. Install Zod (or Valibot, ArkType, …) yourself. Permix only depends on the Standard Schema type protocol.
+- Complements Drizzle: Drizzle answers “which tables and actions?”; Standard Schema answers “what does the entity look like?”
diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json
index dbd79bfc..35996ab7 100644
--- a/docs/content/docs/meta.json
+++ b/docs/content/docs/meta.json
@@ -31,6 +31,7 @@
"integrations/fastify",
"integrations/effect",
"integrations/drizzle",
+ "integrations/standard-schema",
"---",
"[Examples](https://github.com/letstri/permix/tree/main/examples)",
"---LLMs---",
diff --git a/docs/content/docs/migration-v3-to-v4.mdx b/docs/content/docs/migration-v3-to-v4.mdx
index 20be84ca..d7eb125b 100644
--- a/docs/content/docs/migration-v3-to-v4.mdx
+++ b/docs/content/docs/migration-v3-to-v4.mdx
@@ -336,15 +336,16 @@ Map each Better Auth role to a rules object yourself — the same booleans you p
These are additive — migrate the core API first, then adopt what you need:
-| Import | Use case |
-| ----------------------- | ------------------------------------------- |
-| `permix/next` | Next.js App Router, request-scoped instance |
-| `permix/tanstack-start` | TanStack Start middleware and SSR |
-| `permix/server` | Framework-agnostic fetch middleware |
-| `permix/svelte` | Svelte 5 runes |
-| `permix/drizzle` | Rules from Drizzle v1 schema |
-| `permix/drizzle/legacy` | Drizzle v0 (`>=0.30 <1`) |
-| `permix/effect` | Effect `Layer` / `Context` |
+| Import | Use case |
+| ------------------------ | ------------------------------------------- |
+| `permix/next` | Next.js App Router, request-scoped instance |
+| `permix/tanstack-start` | TanStack Start middleware and SSR |
+| `permix/server` | Framework-agnostic fetch middleware |
+| `permix/svelte` | Svelte 5 runes |
+| `permix/drizzle` | Rules from Drizzle v1 schema |
+| `permix/drizzle/legacy` | Drizzle v0 (`>=0.30 <1`) |
+| `permix/standard-schema` | Entity types from Zod, Valibot, ArkType, … |
+| `permix/effect` | Effect `Layer` / `Context` |
See the [examples directory](https://github.com/letstri/permix/tree/main/examples) (`next`, `tanstack-start`, `svelte`, `rebac`, and updated `react`, `vue`, …).
diff --git a/docs/content/docs/quick-start.mdx b/docs/content/docs/quick-start.mdx
index c6bc2196..4b2eaa44 100644
--- a/docs/content/docs/quick-start.mdx
+++ b/docs/content/docs/quick-start.mdx
@@ -194,6 +194,10 @@ Continuing from the quick start, you can now explore how Permix integrates with
Schema-driven permissions from Drizzle tables.
+
+ Entity types from Zod, Valibot, ArkType, and other Standard Schema validators.
+
+
Integration with Effect services and layers.
diff --git a/docs/package.json b/docs/package.json
index 7b7c16f4..2e97be98 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -48,6 +48,7 @@
"hono": "^4.12.23",
"nitro": "^3.0.260522-beta",
"tailwindcss": "^4.3.0",
- "typescript": "^6.0.3"
+ "typescript": "^6.0.3",
+ "zod": "^4.4.3"
}
}
diff --git a/permix/package.json b/permix/package.json
index dfbefd33..35ef1e71 100644
--- a/permix/package.json
+++ b/permix/package.json
@@ -105,6 +105,10 @@
"types": "./dist/drizzle/legacy/index.d.mts",
"import": "./dist/drizzle/legacy/index.mjs"
},
+ "./standard-schema": {
+ "types": "./dist/standard-schema/index.d.mts",
+ "import": "./dist/standard-schema/index.mjs"
+ },
"./effect": {
"types": "./dist/effect/index.d.mts",
"import": "./dist/effect/index.mjs"
@@ -153,6 +157,7 @@
"svelte-check": "^4.5.0",
"tsdown": "^0.22.1",
"typescript": "^6.0.3",
+ "valibot": "^1.4.2",
"vite-plugin-solid": "^2.11.12",
"vitest": "^4.1.8",
"vue": "^3.5.17",
diff --git a/permix/skills/README.md b/permix/skills/README.md
index f2616977..56e319ef 100644
--- a/permix/skills/README.md
+++ b/permix/skills/README.md
@@ -59,6 +59,7 @@ The package includes the `tanstack-intent` npm keyword. Published versions are i
| --- | --- |
| Effect | https://permix.letstri.dev/docs/integrations/effect |
| Drizzle ORM | https://permix.letstri.dev/docs/integrations/drizzle |
+| Standard Schema (Zod, Valibot, …) | https://permix.letstri.dev/docs/integrations/standard-schema |
| Events (`hook`, `hookOnce`) | https://permix.letstri.dev/docs/guide/events |
Examples: https://github.com/letstri/permix/tree/main/examples
diff --git a/permix/skills/permix-getting-started/SKILL.md b/permix/skills/permix-getting-started/SKILL.md
index 9647154a..335a541e 100644
--- a/permix/skills/permix-getting-started/SKILL.md
+++ b/permix/skills/permix-getting-started/SKILL.md
@@ -14,6 +14,7 @@ sources:
- 'letstri/permix:docs/content/docs/guide/instance.mdx'
- 'letstri/permix:docs/content/docs/guide/events.mdx'
- 'letstri/permix:docs/content/docs/migration-v3-to-v4.mdx'
+ - 'letstri/permix:docs/content/docs/integrations/standard-schema.mdx'
- 'letstri/permix:permix/src/core/index.ts'
---
@@ -62,6 +63,23 @@ export const permix = createPermix<{
}>()
```
+**Entity data from a validator** (Zod, Valibot, ArkType, … via [Standard Schema](https://permix.letstri.dev/docs/integrations/standard-schema)):
+
+```ts
+import { action, createPermix } from 'permix'
+import { z } from 'zod'
+
+const postSchema = z.object({ id: z.string(), authorId: z.string() })
+
+const definition = {
+ post: ['create', action('edit', postSchema, { required: true })],
+} as const
+
+export const permix = createPermix()
+```
+
+Or `{ name: 'edit'; schema: typeof postSchema }` on the generic. CRUD-from-schemas: `createPermix` from `permix/standard-schema`. `check()` does not parse data at runtime.
+
Every action you declare in `D` must appear in every `setup()` call (use `false` to deny).
## 2. Assign rules with `setup`
diff --git a/permix/skills/permix/references/check.md b/permix/skills/permix/references/check.md
index e907e799..5c55141a 100644
--- a/permix/skills/permix/references/check.md
+++ b/permix/skills/permix/references/check.md
@@ -66,6 +66,8 @@ permix.check('post.update', post) // optional data if not required: true
permix.check('post.delete', post) // required: true — data required
```
+Prefer `schema: typeof postSchema` (or `action('edit', postSchema)`) when the entity already has a Zod/Valibot/ArkType schema — see https://permix.letstri.dev/docs/integrations/standard-schema. `check()` still does not parse data.
+
**ReBAC pattern**: capture the **actor** in closures at `setup` time; pass the **resource** at `check` time. No separate ReBAC API.
## Readiness
diff --git a/permix/skills/permix/references/server.md b/permix/skills/permix/references/server.md
index 8b8c79d5..e484de6b 100644
--- a/permix/skills/permix/references/server.md
+++ b/permix/skills/permix/references/server.md
@@ -83,10 +83,11 @@ app.get('/posts/:id', (req, res) => {
| Elysia | `permix/elysia` |
| Effect | `permix/effect` — see integration docs |
| Drizzle ORM | `permix/drizzle` (and `permix/drizzle/legacy`) — see integration docs |
+| Standard Schema | `permix/standard-schema` — Zod/Valibot entity types; see integration docs |
Use the same `D` schema shape as the client instance.
-Effect and Drizzle are optional peer dependencies; follow https://permix.letstri.dev/docs/integrations/effect and https://permix.letstri.dev/docs/integrations/drizzle rather than inventing middleware patterns.
+Effect and Drizzle are optional peer dependencies; Standard Schema needs no extra Permix peer (install Zod/Valibot yourself). Follow https://permix.letstri.dev/docs/integrations/effect, https://permix.letstri.dev/docs/integrations/drizzle, and https://permix.letstri.dev/docs/integrations/standard-schema rather than inventing middleware patterns.
## tRPC / oRPC
diff --git a/permix/src/core/definitions.ts b/permix/src/core/definitions.ts
index 624960db..c6ba4fc6 100644
--- a/permix/src/core/definitions.ts
+++ b/permix/src/core/definitions.ts
@@ -1,11 +1,35 @@
+import type {
+ InferStandardSchemaOutput,
+ StandardSchemaV1,
+} from './standard-schema'
+
export interface ActionSpec {
name: string
type?: unknown
+ /**
+ * A Standard Schema (Zod, Valibot, ArkType, Effect Schema, …). Entity data
+ * for this action is inferred as {@link InferStandardSchemaOutput}.
+ * Ignored when {@link type} is also set — `type` wins.
+ */
+ schema?: StandardSchemaV1
required?: boolean
}
export type Action = string | ActionSpec
+/**
+ * Entity data type for an action: {@link ActionSpec.type} if present, else
+ * {@link InferStandardSchemaOutput} of {@link ActionSpec.schema}. `never`
+ * when the action is a plain string (no entity data).
+ */
+export type ActionData = A extends string
+ ? never
+ : A extends { type: infer T }
+ ? T
+ : A extends { schema: infer S extends StandardSchemaV1 }
+ ? InferStandardSchemaOutput
+ : never
+
/**
* The full type of a permissions tree passed to `createPermix()`.
*
diff --git a/permix/src/core/index.ts b/permix/src/core/index.ts
index a0a49baf..8529d0b1 100644
--- a/permix/src/core/index.ts
+++ b/permix/src/core/index.ts
@@ -5,4 +5,5 @@ export * from './hooks'
export type * from './merge'
export * from './permix'
export * from './rules'
+export * from './standard-schema'
export * from './template'
diff --git a/permix/src/core/permix.ts b/permix/src/core/permix.ts
index 6396a46d..5f460619 100644
--- a/permix/src/core/permix.ts
+++ b/permix/src/core/permix.ts
@@ -1,6 +1,6 @@
import type { CheckArgs, CheckContext } from './check'
import { createCheck, createCheckContext } from './check'
-import type { Action, ActionName, Definition } from './definitions'
+import type { Action, ActionData, ActionName, Definition } from './definitions'
import { PermixNotReadyError } from './errors'
import { createHooks } from './hooks'
import type { DehydratedState, Rules } from './rules'
@@ -9,11 +9,11 @@ import { createTemplate } from './template'
export type { DehydratedState, Rules } from './rules'
-type ActionArgs = A extends { type: infer T; required: true }
- ? [T]
- : A extends { type: infer T }
- ? [T?]
- : []
+type ActionArgs = [ActionData ] extends [never]
+ ? []
+ : A extends { required: true }
+ ? [ActionData ]
+ : [ActionData ?]
type ActionByName = A extends unknown
? ActionName extends N
@@ -317,6 +317,16 @@ export interface Permix {
* permix.check('post.create') // true
* permix.check('post.edit', { authorId: '1' }) // true/false
* ```
+ *
+ * @example Standard Schema (Zod, Valibot, …)
+ * ```ts
+ * const permix = createPermix<{
+ * post: [
+ * 'create',
+ * { name: 'edit', schema: typeof postSchema, required: true },
+ * ]
+ * }>()
+ * ```
*/
export function createPermix(
initialRules?: Rules
diff --git a/permix/src/core/rules.ts b/permix/src/core/rules.ts
index 3ecbd78b..3896d9f4 100644
--- a/permix/src/core/rules.ts
+++ b/permix/src/core/rules.ts
@@ -1,11 +1,11 @@
import { callRuleWithoutData } from './check'
-import type { Action, ActionName, Definition } from './definitions'
+import type { Action, ActionData, ActionName, Definition } from './definitions'
-type ActionRule = A extends { type: infer T; required: true }
- ? (data: T) => boolean
- : A extends { type: infer T }
- ? ((data?: T) => boolean) | boolean
- : boolean | (() => boolean)
+type ActionRule = [ActionData ] extends [never]
+ ? boolean | (() => boolean)
+ : A extends { required: true }
+ ? (data: ActionData ) => boolean
+ : ((data?: ActionData ) => boolean) | boolean
/**
* The shape of the object passed to `permix.setup()` (and produced by
diff --git a/permix/src/core/standard-schema.test.ts b/permix/src/core/standard-schema.test.ts
new file mode 100644
index 00000000..2ec43f4a
--- /dev/null
+++ b/permix/src/core/standard-schema.test.ts
@@ -0,0 +1,149 @@
+import * as v from 'valibot'
+import { describe, expect, expectTypeOf, it } from 'vitest'
+import { z } from 'zod'
+
+import { createPermix } from './permix'
+import { action } from './standard-schema'
+
+const postSchema = z.object({
+ id: z.string(),
+ authorId: z.string(),
+})
+
+const valibotPostSchema = v.object({
+ id: v.string(),
+ authorId: v.string(),
+})
+
+describe('ActionSpec.schema', () => {
+ it('infers entity data from a Zod schema on the action spec', () => {
+ const permix = createPermix<{
+ post: [
+ 'create',
+ { name: 'edit'; schema: typeof postSchema; required: true },
+ ]
+ }>()
+
+ permix.setup({
+ post: {
+ create: true,
+ edit: (post) => {
+ expectTypeOf(post).toEqualTypeOf<{ id: string; authorId: string }>()
+ return post.authorId === '1'
+ },
+ },
+ })
+
+ expectTypeOf(permix.check)
+ .parameter(0)
+ .extract<'post.edit'>()
+ .toEqualTypeOf<'post.edit'>()
+
+ expect(permix.check('post.edit', { id: 'p1', authorId: '1' })).toBe(true)
+ expect(permix.check('post.edit', { id: 'p1', authorId: '2' })).toBe(false)
+ // @ts-expect-error data is required
+ expect(() => permix.check('post.edit')).toThrow()
+ })
+
+ it('lets type override schema when both are set', () => {
+ const permix = createPermix<{
+ post: [
+ {
+ name: 'edit'
+ schema: typeof postSchema
+ type: { ownerId: string }
+ required: true
+ },
+ ]
+ }>()
+
+ permix.setup({
+ post: {
+ edit: (post) => post.ownerId === '1',
+ },
+ })
+
+ expect(permix.check('post.edit', { ownerId: '1' })).toBe(true)
+ expect(permix.check('post.edit', { ownerId: '2' })).toBe(false)
+ })
+
+ it('does not parse check data at runtime', () => {
+ const permix = createPermix<{
+ post: [{ name: 'edit'; schema: typeof postSchema }]
+ }>()
+
+ permix.setup({
+ post: {
+ edit: (post) => post?.authorId === '1',
+ },
+ })
+
+ expect(
+ permix.check('post.edit', {
+ id: 'p1',
+ authorId: '1',
+ extra: true,
+ } as { id: string; authorId: string })
+ ).toBe(true)
+ })
+})
+
+describe('action()', () => {
+ it('builds a definition whose schema types flow into check and setup', () => {
+ const definition = {
+ post: ['create', action('edit', postSchema, { required: true })],
+ } as const
+
+ const permix = createPermix()
+
+ permix.setup({
+ post: {
+ create: true,
+ edit: (post) => post.authorId === '1',
+ },
+ })
+
+ expect(permix.check('post.create')).toBe(true)
+ expect(permix.check('post.edit', { id: 'p1', authorId: '1' })).toBe(true)
+ expect(permix.check('post.edit', { id: 'p1', authorId: '2' })).toBe(false)
+ // @ts-expect-error data is required
+ expect(() => permix.check('post.edit')).toThrow()
+ })
+
+ it('keeps check data optional when required is omitted', () => {
+ const definition = {
+ post: [action('edit', postSchema)],
+ } as const
+
+ const permix = createPermix()
+
+ permix.setup({
+ post: {
+ edit: (post) => post?.authorId === '1',
+ },
+ })
+
+ expect(permix.check('post.edit')).toBe(false)
+ expect(permix.check('post.edit', { id: 'p1', authorId: '1' })).toBe(true)
+ })
+})
+
+describe('ActionSpec.schema with Valibot', () => {
+ it('infers entity data from a Valibot schema', () => {
+ const permix = createPermix<{
+ post: [{ name: 'edit'; schema: typeof valibotPostSchema; required: true }]
+ }>()
+
+ permix.setup({
+ post: {
+ edit: (post) => {
+ expectTypeOf(post).toEqualTypeOf<{ id: string; authorId: string }>()
+ return post.authorId === '1'
+ },
+ },
+ })
+
+ expect(permix.check('post.edit', { id: 'p1', authorId: '1' })).toBe(true)
+ expect(permix.check('post.edit', { id: 'p1', authorId: '2' })).toBe(false)
+ })
+})
diff --git a/permix/src/core/standard-schema.ts b/permix/src/core/standard-schema.ts
new file mode 100644
index 00000000..5af0378b
--- /dev/null
+++ b/permix/src/core/standard-schema.ts
@@ -0,0 +1,103 @@
+/**
+ * Standard Schema V1 types, vendored from
+ * {@link https://standardschema.dev | standardschema.dev} so Permix can infer
+ * entity data from Zod, Valibot, ArkType, Effect Schema, and other
+ * Standard Schema implementations without taking a runtime dependency.
+ *
+ * Flattened from the spec's `StandardSchemaV1` namespace to match this repo's
+ * lint rules. The `~standard` protocol is type-only here. Permix does not
+ * parse `check()` data at runtime.
+ */
+
+export interface StandardSchemaV1Types {
+ readonly input: Input
+ readonly output: Output
+}
+
+export interface StandardSchemaV1PathSegment {
+ readonly key: PropertyKey
+}
+
+export interface StandardSchemaV1Issue {
+ readonly message: string
+ readonly path?:
+ | readonly (PropertyKey | StandardSchemaV1PathSegment)[]
+ | undefined
+}
+
+export interface StandardSchemaV1SuccessResult {
+ readonly value: Output
+ readonly issues?: undefined
+}
+
+export interface StandardSchemaV1FailureResult {
+ readonly issues: readonly StandardSchemaV1Issue[]
+}
+
+export type StandardSchemaV1Result =
+ | StandardSchemaV1SuccessResult
+ | StandardSchemaV1FailureResult
+
+export interface StandardSchemaV1Props {
+ readonly version: 1
+ readonly vendor: string
+ readonly validate: (
+ value: unknown
+ ) => StandardSchemaV1Result | Promise>
+ readonly types?: StandardSchemaV1Types | undefined
+}
+
+/** The Standard Schema interface. */
+export interface StandardSchemaV1 {
+ readonly '~standard': StandardSchemaV1Props
+}
+
+/** Infers the input type of a Standard Schema. */
+export type InferStandardSchemaInput =
+ NonNullable['input']
+
+/** Infers the output type of a Standard Schema. */
+export type InferStandardSchemaOutput =
+ NonNullable['output']
+
+export interface ActionSchemaOptions {
+ required?: R
+}
+
+/**
+ * Build an {@link import('./definitions').ActionSpec} whose entity data type
+ * is inferred from a Standard Schema (Zod, Valibot, ArkType, …).
+ *
+ * Use with `createPermix()` and `as const` so action names
+ * stay literal.
+ *
+ * @example
+ * ```ts
+ * const postSchema = z.object({ id: z.string(), authorId: z.string() })
+ *
+ * const definition = {
+ * post: [
+ * 'create',
+ * action('edit', postSchema, { required: true }),
+ * ],
+ * } as const
+ *
+ * const permix = createPermix()
+ * ```
+ */
+export function action<
+ const N extends string,
+ S extends StandardSchemaV1,
+ const R extends boolean = false,
+>(
+ name: N,
+ schema: S,
+ options?: ActionSchemaOptions
+): R extends true
+ ? { readonly name: N; readonly schema: S; readonly required: true }
+ : { readonly name: N; readonly schema: S } {
+ if (options?.required === true) {
+ return { name, schema, required: true as const } as never
+ }
+ return { name, schema } as never
+}
diff --git a/permix/src/standard-schema/errors.ts b/permix/src/standard-schema/errors.ts
new file mode 100644
index 00000000..e2bcd680
--- /dev/null
+++ b/permix/src/standard-schema/errors.ts
@@ -0,0 +1,20 @@
+import { PermixError } from '../core/errors'
+
+export class PermixInvalidActionsError extends PermixError {
+ constructor() {
+ super('`actions` must be a non-empty array of strings.')
+ this.name = 'PermixInvalidActionsError'
+ }
+}
+
+export class PermixInvalidSchemaMapError extends PermixError {
+ key: string
+
+ constructor(key: string) {
+ super(
+ `Invalid standard-schema map value at "${key}". Expected a Standard Schema, entity(schema, actions), or an action name tuple.`
+ )
+ this.name = 'PermixInvalidSchemaMapError'
+ this.key = key
+ }
+}
diff --git a/permix/src/standard-schema/index.ts b/permix/src/standard-schema/index.ts
new file mode 100644
index 00000000..1bb0acc7
--- /dev/null
+++ b/permix/src/standard-schema/index.ts
@@ -0,0 +1,2 @@
+export * from './errors'
+export * from './permix'
diff --git a/permix/src/standard-schema/permix.test.ts b/permix/src/standard-schema/permix.test.ts
new file mode 100644
index 00000000..cbbe957f
--- /dev/null
+++ b/permix/src/standard-schema/permix.test.ts
@@ -0,0 +1,152 @@
+import * as v from 'valibot'
+import { describe, expect, expectTypeOf, it } from 'vitest'
+import { z } from 'zod'
+
+import { PermixRuleNotDefinedError } from '../core/errors'
+import {
+ PermixInvalidActionsError,
+ PermixInvalidSchemaMapError,
+} from './errors'
+import { createPermix, entity } from './permix'
+
+const postSchema = z.object({
+ id: z.string(),
+ authorId: z.string(),
+})
+
+const commentSchema = z.object({
+ id: z.string(),
+ postId: z.string(),
+})
+
+const valibotPostSchema = v.object({
+ id: v.string(),
+ authorId: v.string(),
+})
+
+describe('standard-schema createPermix', () => {
+ it('creates CRUD entities from a Zod schema map', () => {
+ const permix = createPermix({
+ post: postSchema,
+ comment: commentSchema,
+ })
+
+ expect(permix.entities).toStrictEqual(['post', 'comment'])
+ expect(permix.actions).toStrictEqual(['create', 'read', 'update', 'delete'])
+
+ permix.setup({
+ post: {
+ create: true,
+ read: true,
+ update: (post) => {
+ expectTypeOf(post).toEqualTypeOf<
+ { id: string; authorId: string } | undefined
+ >()
+ return post?.authorId === '1'
+ },
+ delete: false,
+ },
+ comment: {
+ create: true,
+ read: true,
+ update: false,
+ delete: false,
+ },
+ })
+
+ expect(permix.check('post.create')).toBe(true)
+ expect(permix.check('post.delete')).toBe(false)
+ expect(permix.check('post.update', { id: 'p1', authorId: '1' })).toBe(true)
+ expect(permix.check('post.update', { id: 'p1', authorId: '2' })).toBe(false)
+ expect(permix.check('comment.read')).toBe(true)
+ })
+
+ it('supports a custom action set via the actions option', () => {
+ const permix = createPermix(
+ { post: postSchema },
+ { actions: ['view', 'edit'] }
+ )
+
+ expect(permix.actions).toStrictEqual(['view', 'edit'])
+
+ permix.setup({
+ post: { view: true, edit: false },
+ })
+
+ expect(permix.check('post.view')).toBe(true)
+ expect(permix.check('post.edit')).toBe(false)
+ // @ts-expect-error 'create' is not in the custom action set
+ expect(() => permix.check('post.create')).toThrow(PermixRuleNotDefinedError)
+ })
+
+ it('lets entity() customise actions and require data per action', () => {
+ const permix = createPermix({
+ post: entity(postSchema, [
+ 'create',
+ 'read',
+ { name: 'publish', required: true },
+ ]),
+ dashboard: ['view'] as const,
+ })
+
+ permix.setup({
+ post: {
+ create: true,
+ read: true,
+ publish: (post) => post.authorId === '1',
+ },
+ dashboard: { view: true },
+ })
+
+ expect(permix.check('post.create')).toBe(true)
+ expect(permix.check('dashboard.view')).toBe(true)
+ expect(permix.check('post.publish', { id: 'p1', authorId: '1' })).toBe(true)
+ // @ts-expect-error data is required
+ expect(() => permix.check('post.publish')).toThrow()
+ // @ts-expect-error untyped action list has no entity data
+ expect(permix.check('dashboard.view', { id: 'x' })).toBe(true)
+ })
+
+ it('accepts Valibot schemas in the map', () => {
+ const permix = createPermix({ post: valibotPostSchema })
+
+ permix.setup({
+ post: {
+ create: true,
+ read: true,
+ update: (post) => post?.authorId === '1',
+ delete: false,
+ },
+ })
+
+ expect(permix.check('post.update', { id: 'p1', authorId: '1' })).toBe(true)
+ })
+
+ it('throws when actions is an empty array', () => {
+ expect(() => createPermix({ post: postSchema }, { actions: [] })).toThrow(
+ PermixInvalidActionsError
+ )
+ })
+
+ it('throws when entity() is given an empty action list', () => {
+ expect(() => entity(postSchema, [])).toThrow(PermixInvalidActionsError)
+ })
+
+ it('throws when a map value is not a schema, entity(), or action tuple', () => {
+ expect(() =>
+ createPermix({
+ post: postSchema,
+ bad: { hello: true },
+ } as never)
+ ).toThrow(PermixInvalidSchemaMapError)
+ })
+
+ it('exposes entity and action types', () => {
+ const permix = createPermix({ post: postSchema, comment: commentSchema })
+
+ expectTypeOf(permix.entities).toEqualTypeOf<('post' | 'comment')[]>()
+ expectTypeOf(permix.actions).toEqualTypeOf<
+ readonly ['create', 'read', 'update', 'delete']
+ >()
+ })
+})
diff --git a/permix/src/standard-schema/permix.ts b/permix/src/standard-schema/permix.ts
new file mode 100644
index 00000000..daad3a90
--- /dev/null
+++ b/permix/src/standard-schema/permix.ts
@@ -0,0 +1,266 @@
+import type { Permix as PermixCore } from '../core'
+import { createPermix as createPermixCore } from '../core'
+import type { Action } from '../core/definitions'
+import type {
+ InferStandardSchemaOutput,
+ StandardSchemaV1,
+} from '../core/standard-schema'
+import {
+ PermixInvalidActionsError,
+ PermixInvalidSchemaMapError,
+} from './errors'
+
+/**
+ * The default CRUD action set used when no `actions` are provided.
+ */
+export const DEFAULT_STANDARD_SCHEMA_ACTIONS = [
+ 'create',
+ 'read',
+ 'update',
+ 'delete',
+] as const
+
+export type DefaultStandardSchemaAction =
+ (typeof DEFAULT_STANDARD_SCHEMA_ACTIONS)[number]
+
+/**
+ * An action name, or a named spec that marks entity data as required.
+ * The entity type is taken from the schema passed to {@link entity} or the
+ * map value — do not repeat `type` / `schema` here.
+ */
+export type EntityAction = string | { name: string; required?: boolean }
+
+export interface EntityConfig<
+ S extends StandardSchemaV1 = StandardSchemaV1,
+ Actions extends readonly EntityAction[] = readonly EntityAction[],
+> {
+ readonly schema: S
+ readonly actions: Actions
+}
+
+export type SchemaMapValue = StandardSchemaV1 | EntityConfig | readonly Action[]
+
+export type SchemaMap = Record