Fix local dev seeding, redirects, and package pinning (#2169)

* feat: seed local owner content for dev

* fix: redirect merged skill pages to canonical slug

* chore: pin package dependency versions

* fix: remove dashboard seed button

* docs: document local fixture seeding

* fix: make current-user seed fixtures internal
This commit is contained in:
Jason
2026-05-13 16:41:31 -07:00
committed by GitHub
parent 9fcf892e34
commit 7e1e2f0a4f
15 changed files with 891 additions and 277 deletions
+11 -3
View File
@@ -82,16 +82,23 @@ Change the port if 3000 is already in use, and update `SITE_URL` in both `.env.l
### Seed the database
Populate sample data so the UI isn't empty:
Populate shared `@local` sample skills, plugins, and scanner fixtures so the UI is not empty:
```bash
# 3 sample skills (padel, gohome, xuezh)
bun run seed:dev
```
The script waits for the local Convex deployment, runs the fixture seed, and refreshes global stats.
If you need to run the pieces manually:
```bash
# Skills, plugins, and moderation/scanner fixtures
bunx convex run --no-push devSeed:seedNixSkills
# 50 extra skills for pagination testing (optional)
bunx convex run --no-push devSeedExtra:seedExtraSkillsInternal
# Refresh the cached skills count (required after seeding)
# Refresh cached global stats after manual seeding
bunx convex run --no-push statsMaintenance:updateGlobalStatsAction
```
@@ -99,6 +106,7 @@ To reset and re-seed:
```bash
bunx convex run --no-push devSeed:seedNixSkills '{"reset": true}'
bunx convex run --no-push statsMaintenance:updateGlobalStatsAction
```
### Optional environment variables
+5 -2
View File
@@ -108,11 +108,14 @@ bun run dev
# detached/Codex worktree preview
bun run dev:worktree
# seed sample data
# optional: seed local skills, plugins, and scanner fixtures
bun run seed:dev
```
For full setup instructions (env vars, GitHub OAuth, JWT keys, database seeding), see [CONTRIBUTING.md](CONTRIBUTING.md).
`bun run seed:dev` waits for the local Convex deployment, runs the dev fixture seed, and refreshes
global stats. The fixtures are owned by `@local` and are safe to rerun after fixture or schema
changes. For reset/manual commands and full setup instructions (env vars, GitHub OAuth, JWT keys,
database seeding), see [CONTRIBUTING.md](CONTRIBUTING.md).
## Environment
+127 -136
View File
@@ -1,80 +1,79 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "clawhub",
"dependencies": {
"@auth/core": "^0.37.4",
"@auth/core": "0.37.4",
"@convex-dev/auth": "0.0.92",
"@fontsource/bricolage-grotesque": "^5.2.10",
"@fontsource/ibm-plex-mono": "^5.2.7",
"@fontsource/manrope": "^5.2.8",
"@monaco-editor/react": "^4.7.0",
"@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"@resvg/resvg-wasm": "^2.6.2",
"@shikijs/rehype": "^4.0.2",
"@fontsource/bricolage-grotesque": "5.2.10",
"@fontsource/ibm-plex-mono": "5.2.7",
"@fontsource/manrope": "5.2.8",
"@monaco-editor/react": "4.7.0",
"@radix-ui/react-avatar": "1.1.11",
"@radix-ui/react-dialog": "1.1.15",
"@radix-ui/react-dropdown-menu": "2.1.16",
"@radix-ui/react-label": "2.1.8",
"@radix-ui/react-select": "2.2.6",
"@radix-ui/react-separator": "1.1.8",
"@radix-ui/react-slot": "1.2.4",
"@radix-ui/react-toggle-group": "1.1.11",
"@radix-ui/react-tooltip": "1.2.8",
"@resvg/resvg-wasm": "2.6.2",
"@shikijs/rehype": "4.0.2",
"@tanstack/react-router": "1.169.2",
"@tanstack/react-start": "1.167.65",
"@vercel/analytics": "^2.0.1",
"class-variance-authority": "^0.7.1",
"clawhub-schema": "workspace:*",
"clsx": "^2.1.1",
"convex": "^1.38.0",
"convex-helpers": "^0.1.116",
"fflate": "^0.8.2",
"@vercel/analytics": "2.0.1",
"class-variance-authority": "0.7.1",
"clawhub-schema": "workspace:0.0.2",
"clsx": "2.1.1",
"convex": "1.38.0",
"convex-helpers": "0.1.116",
"fflate": "0.8.2",
"h3": "2.0.1-rc.22",
"ignore": "^7.0.5",
"ignore": "7.0.5",
"lucide-react": "1.14.0",
"mime": "^4.1.0",
"monaco-editor": "^0.55.1",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"react-markdown": "^10.1.0",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.1",
"semver": "^7.8.0",
"shiki": "^4.0.2",
"sonner": "^2.0.7",
"tailwind-merge": "^3.6.0",
"tailwindcss": "^4.3.0",
"tw-animate-css": "^1.4.0",
"unified": "^11.0.5",
"unist-util-visit": "^5.1.0",
"yaml": "^2.9.0",
"mime": "4.1.0",
"monaco-editor": "0.55.1",
"react": "19.2.6",
"react-dom": "19.2.6",
"react-markdown": "10.1.0",
"rehype-raw": "7.0.0",
"rehype-sanitize": "6.0.0",
"remark-gfm": "4.0.1",
"semver": "7.8.0",
"shiki": "4.0.2",
"sonner": "2.0.7",
"tailwind-merge": "3.6.0",
"tailwindcss": "4.3.0",
"tw-animate-css": "1.4.0",
"unified": "11.0.5",
"unist-util-visit": "5.1.0",
"yaml": "2.9.0",
"zod": "4.4.3",
},
"devDependencies": {
"@playwright/test": "^1.60.0",
"@tailwindcss/vite": "^4.3.0",
"@playwright/test": "1.60.0",
"@tailwindcss/vite": "4.3.0",
"@tanstack/devtools-vite": "0.6.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.2",
"@types/node": "^25.7.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@types/semver": "^7.7.1",
"@testing-library/dom": "10.4.1",
"@testing-library/react": "16.3.2",
"@types/node": "25.7.0",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"@types/semver": "7.7.1",
"@vitejs/plugin-react": "6.0.1",
"@vitest/coverage-v8": "^4.1.6",
"jsdom": "^29.1.1",
"@vitest/coverage-v8": "4.1.6",
"jsdom": "29.1.1",
"nitro": "3.0.260429-beta",
"only-allow": "^1.2.2",
"only-allow": "1.2.2",
"oxfmt": "0.49.0",
"oxlint": "^1.64.0",
"oxlint": "1.64.0",
"oxlint-tsgolint": "0.22.1",
"typescript": "6.0.3",
"undici": "7.25.0",
"vite": "8.0.12",
"vitest": "^4.1.6",
"vitest": "4.1.6",
},
},
"packages/clawhub": {
@@ -85,20 +84,20 @@
"clawhub": "bin/clawdhub.js",
},
"dependencies": {
"@clack/prompts": "^1.4.0",
"arktype": "^2.2.0",
"commander": "^14.0.3",
"fflate": "^0.8.2",
"ignore": "^7.0.5",
"json5": "^2.2.3",
"mime": "^4.1.0",
"ora": "^9.4.0",
"@clack/prompts": "1.4.0",
"arktype": "2.2.0",
"commander": "14.0.3",
"fflate": "0.8.2",
"ignore": "7.0.5",
"json5": "2.2.3",
"mime": "4.1.0",
"ora": "9.4.0",
"p-retry": "8.0.0",
"semver": "^7.8.0",
"semver": "7.8.0",
"undici": "7.25.0",
},
"devDependencies": {
"@types/node": "^25.7.0",
"@types/node": "25.7.0",
"typescript": "6.0.3",
},
},
@@ -109,20 +108,20 @@
"clawhub-mod": "bin/clawhub-mod.js",
},
"dependencies": {
"@clack/prompts": "^1.4.0",
"arktype": "^2.2.0",
"commander": "^14.0.3",
"fflate": "^0.8.2",
"ignore": "^7.0.5",
"json5": "^2.2.3",
"mime": "^4.1.0",
"ora": "^9.4.0",
"@clack/prompts": "1.4.0",
"arktype": "2.2.0",
"commander": "14.0.3",
"fflate": "0.8.2",
"ignore": "7.0.5",
"json5": "2.2.3",
"mime": "4.1.0",
"ora": "9.4.0",
"p-retry": "8.0.0",
"semver": "^7.8.0",
"semver": "7.8.0",
"undici": "7.25.0",
},
"devDependencies": {
"@types/node": "^25.7.0",
"@types/node": "25.7.0",
"typescript": "6.0.3",
},
},
@@ -130,7 +129,7 @@
"name": "clawhub-schema",
"version": "0.0.2",
"dependencies": {
"arktype": "^2.2.0",
"arktype": "2.2.0",
},
"devDependencies": {
"typescript": "6.0.3",
@@ -159,7 +158,7 @@
"@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
"@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="],
"@babel/compat-data": ["@babel/compat-data@7.29.3", "", {}, "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg=="],
"@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="],
@@ -183,7 +182,7 @@
"@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="],
"@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="],
"@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="],
"@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w=="],
@@ -209,13 +208,13 @@
"@csstools/color-helpers": ["@csstools/color-helpers@6.0.2", "", {}, "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q=="],
"@csstools/css-calc": ["@csstools/css-calc@3.2.0", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w=="],
"@csstools/css-calc": ["@csstools/css-calc@3.2.1", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg=="],
"@csstools/css-color-parser": ["@csstools/css-color-parser@4.1.0", "", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "@csstools/css-calc": "^3.2.0" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ=="],
"@csstools/css-color-parser": ["@csstools/css-color-parser@4.1.1", "", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "@csstools/css-calc": "^3.2.1" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g=="],
"@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@4.0.0", "", { "peerDependencies": { "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w=="],
"@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.1.3", "", { "peerDependencies": { "css-tree": "^3.2.1" }, "optionalPeers": ["css-tree"] }, "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg=="],
"@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.1.4", "", { "peerDependencies": { "css-tree": "^3.2.1" }, "optionalPeers": ["css-tree"] }, "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw=="],
"@csstools/css-tokenizer": ["@csstools/css-tokenizer@4.0.0", "", {}, "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA=="],
@@ -327,7 +326,7 @@
"@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="],
"@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="],
"@oxc-project/types": ["@oxc-project/types@0.130.0", "", {}, "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q=="],
"@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.49.0", "", { "os": "android", "cpu": "arm" }, "sha512-HbifJ84prIh9+55CTPAU35JdRQrwg47y16cGerCC+iejSKOuHXYo2WDql6l7cQlzrYVtc3f4UWY+dBj2lRmOeA=="],
@@ -499,35 +498,35 @@
"@resvg/resvg-wasm": ["@resvg/resvg-wasm@2.6.2", "", {}, "sha512-FqALmHI8D4o6lk/LRWDnhw95z5eO+eAa6ORjVg09YRR7BkcM6oPHU9uyC0gtQG5vpFLvgpeU4+zEAz2H8APHNw=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.17", "", { "os": "android", "cpu": "arm64" }, "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.1", "", { "os": "android", "cpu": "arm64" }, "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm" }, "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "s390x" }, "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "x64" }, "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.1", "", { "os": "linux", "cpu": "x64" }, "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.17", "", { "os": "linux", "cpu": "x64" }, "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.17", "", { "os": "none", "cpu": "arm64" }, "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.1", "", { "os": "none", "cpu": "arm64" }, "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ=="],
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.17", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA=="],
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.1", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.17", "", { "os": "win32", "cpu": "x64" }, "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.1", "", { "os": "win32", "cpu": "x64" }, "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.7", "", {}, "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA=="],
@@ -629,7 +628,7 @@
"@testing-library/react": ["@testing-library/react@16.3.2", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="],
"@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="],
@@ -639,7 +638,7 @@
"@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
"@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
@@ -661,7 +660,7 @@
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="],
"@vercel/analytics": ["@vercel/analytics@2.0.1", "", { "peerDependencies": { "@remix-run/react": "^2", "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "nuxt": ">= 3", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@remix-run/react", "@sveltejs/kit", "next", "nuxt", "react", "svelte", "vue", "vue-router"] }, "sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g=="],
@@ -687,7 +686,7 @@
"ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
"ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="],
"ansis": ["ansis@4.3.0", "", {}, "sha512-44mvgtPvohuU/70DdY5Oz2AIrLJ9k6/5x4KmoSvPwO+5Moijo0+N9D0fKbbYZQWP1hNm5CpOf+E01jhxG/r8xg=="],
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
@@ -709,7 +708,7 @@
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.18", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.29", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ=="],
"bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="],
@@ -721,7 +720,7 @@
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
"caniuse-lite": ["caniuse-lite@1.0.30001787", "", {}, "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg=="],
"caniuse-lite": ["caniuse-lite@1.0.30001792", "", {}, "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw=="],
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
@@ -813,17 +812,17 @@
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
"electron-to-chromium": ["electron-to-chromium@1.5.336", "", {}, "sha512-AbH9q9J455r/nLmdNZes0G0ZKcRX73FicwowalLs6ijwOmCJSRRrLX63lcAlzy9ux3dWK1w1+1nsBJEWN11hcQ=="],
"electron-to-chromium": ["electron-to-chromium@1.5.354", "", {}, "sha512-JaBHwWcfIdmSAfWM5l3uwjGd431j8YEMikZ+K/2nXVuBqJKyZ0f+2h4n4JY5AyNiZmnY9qQr2RU3v9DxDmHMNg=="],
"encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="],
"enhanced-resolve": ["enhanced-resolve@5.21.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA=="],
"enhanced-resolve": ["enhanced-resolve@5.21.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q=="],
"entities": ["entities@8.0.0", "", {}, "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA=="],
"env-runner": ["env-runner@0.1.7", "", { "dependencies": { "crossws": "^0.4.4", "exsolve": "^1.0.8", "httpxy": "^0.5.0", "srvx": "^0.11.13" }, "peerDependencies": { "@netlify/runtime": "^4", "miniflare": "^4.20260317.3" }, "optionalPeers": ["@netlify/runtime", "miniflare"], "bin": { "env-runner": "dist/cli.mjs" } }, "sha512-i7h96jxETJYhXy5grgHNJ9xNzCzWIn9Ck/VkkYgOlE4gOqknsLX3CmlVb5LmwNex8sOoLFVZLz+TIw/+b5rktA=="],
"es-module-lexer": ["es-module-lexer@2.0.0", "", {}, "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw=="],
"es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="],
"esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="],
@@ -859,7 +858,7 @@
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
"get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="],
"get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="],
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
@@ -929,7 +928,7 @@
"is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="],
"is-network-error": ["is-network-error@1.3.1", "", {}, "sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw=="],
"is-network-error": ["is-network-error@1.3.2", "", {}, "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA=="],
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
@@ -939,7 +938,7 @@
"is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
"isbot": ["isbot@5.1.38", "", {}, "sha512-Cus2702JamTNMEY4zTP+TShgq/3qzjvGcBC4XMOV45BLaxD4iUFENkqu7ZhFeSzwNsCSZLjnGlihDQznnpnEEA=="],
"isbot": ["isbot@5.1.40", "", {}, "sha512-yNeeynhhtIVRBk12tBV4eHNxwB42HzR4Q3Ea7vCOiJhImGaAIdIMrbJtacQlBizGLjUPw+akkFI5Dn9T70XoVQ=="],
"istanbul-lib-coverage": ["istanbul-lib-coverage@3.2.2", "", {}, "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg=="],
@@ -947,7 +946,7 @@
"istanbul-reports": ["istanbul-reports@3.2.0", "", { "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" } }, "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA=="],
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
"jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
"jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="],
@@ -993,7 +992,7 @@
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
"lru-cache": ["lru-cache@11.3.5", "", {}, "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw=="],
"lru-cache": ["lru-cache@11.3.6", "", {}, "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A=="],
"lucia": ["lucia@3.2.2", "", { "dependencies": { "@oslojs/crypto": "^1.0.1", "@oslojs/encoding": "^1.1.0" } }, "sha512-P1FlFBGCMPMXu+EGdVD9W4Mjm0DqsusmKgO7Xc33mI5X1bklmsQb0hfzPhXomQr9waWIBDsiOjvr1e6BTaUqpA=="],
@@ -1107,19 +1106,19 @@
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
"nf3": ["nf3@0.3.16", "", {}, "sha512-Gs0xRPpUm2nDkqbi40NJ9g7qDIcjcJzgExiydnq6LAyqhI2jfno8wG3NKTL+IiJsx799UHOb1CnSd4Wg4SG4Pw=="],
"nf3": ["nf3@0.3.17", "", {}, "sha512-N9zEWySuJFw+gR0lhS5863YsvNeudOdqRyFvNb+jMXbeTJOdrjDqkCpDginIZfUm0LzT1t1nCRiDeqQm/8kirQ=="],
"nitro": ["nitro@3.0.260429-beta", "", { "dependencies": { "consola": "^3.4.2", "crossws": "^0.4.5", "db0": "^0.3.4", "env-runner": "^0.1.7", "h3": "^2.0.1-rc.20", "hookable": "^6.1.1", "nf3": "^0.3.16", "ocache": "^0.1.4", "ofetch": "^2.0.0-alpha.3", "ohash": "^2.0.11", "rolldown": "^1.0.0-rc.17", "srvx": "^0.11.15", "unenv": "^2.0.0-rc.24", "unstorage": "^2.0.0-alpha.7" }, "peerDependencies": { "@vercel/queue": "^0.1.6", "dotenv": "*", "giget": "*", "jiti": "^2.6.1", "rollup": "^4.60.2", "vite": "^7 || ^8", "xml2js": "^0.6.2", "zephyr-agent": "^0.2.0" }, "optionalPeers": ["@vercel/queue", "dotenv", "giget", "jiti", "rollup", "vite", "xml2js", "zephyr-agent"], "bin": { "nitro": "dist/cli/index.mjs" } }, "sha512-KweLVCUN5X9v9g+4yxAyRcz3FcOlnjmt9FyrAIWDxJETJmNT7I0JV0clgsONjo2nI0U5gwedXYA3RaNtF5XWzg=="],
"node-releases": ["node-releases@2.0.37", "", {}, "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg=="],
"node-releases": ["node-releases@2.0.44", "", {}, "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ=="],
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
"oauth4webapi": ["oauth4webapi@3.8.5", "", {}, "sha512-A8jmyUckVhRJj5lspguklcl90Ydqk61H3dcU0oLhH3Yv13KpAliKTt5hknpGGPZSSfOwGyraNEFmofDYH+1kSg=="],
"oauth4webapi": ["oauth4webapi@3.8.6", "", {}, "sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ=="],
"obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
@@ -1131,9 +1130,9 @@
"onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
"oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="],
"oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="],
"oniguruma-to-es": ["oniguruma-to-es@4.3.5", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ=="],
"oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="],
"only-allow": ["only-allow@1.2.2", "", { "dependencies": { "which-pm-runs": "1.1.0" }, "bin": { "only-allow": "bin.js" } }, "sha512-uxyNYDsCh5YIJ780G7hC5OHjVUr9reHsbZNMM80L9tZlTpb3hUzb36KXgW4ZUGtJKQnGA3xegmWg1BxhWV0jJA=="],
@@ -1173,7 +1172,7 @@
"preact-render-to-string": ["preact-render-to-string@6.5.11", "", { "peerDependencies": { "preact": ">=10" } }, "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw=="],
"prettier": ["prettier@3.8.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q=="],
"prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="],
"pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="],
@@ -1219,7 +1218,7 @@
"restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
"rolldown": ["rolldown@1.0.0-rc.17", "", { "dependencies": { "@oxc-project/types": "=0.127.0", "@rolldown/pluginutils": "1.0.0-rc.17" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-x64": "1.0.0-rc.17", "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA=="],
"rolldown": ["rolldown@1.0.1", "", { "dependencies": { "@oxc-project/types": "=0.130.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.1", "@rolldown/binding-darwin-arm64": "1.0.1", "@rolldown/binding-darwin-x64": "1.0.1", "@rolldown/binding-freebsd-x64": "1.0.1", "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", "@rolldown/binding-linux-arm64-gnu": "1.0.1", "@rolldown/binding-linux-arm64-musl": "1.0.1", "@rolldown/binding-linux-ppc64-gnu": "1.0.1", "@rolldown/binding-linux-s390x-gnu": "1.0.1", "@rolldown/binding-linux-x64-gnu": "1.0.1", "@rolldown/binding-linux-x64-musl": "1.0.1", "@rolldown/binding-openharmony-arm64": "1.0.1", "@rolldown/binding-wasm32-wasi": "1.0.1", "@rolldown/binding-win32-arm64-msvc": "1.0.1", "@rolldown/binding-win32-x64-msvc": "1.0.1" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ=="],
"rou3": ["rou3@0.8.1", "", {}, "sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA=="],
@@ -1261,11 +1260,11 @@
"state-local": ["state-local@1.0.7", "", {}, "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="],
"std-env": ["std-env@4.0.0", "", {}, "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ=="],
"std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="],
"stdin-discarder": ["stdin-discarder@0.3.2", "", {}, "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A=="],
"string-width": ["string-width@8.2.0", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw=="],
"string-width": ["string-width@8.2.1", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="],
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
@@ -1287,7 +1286,7 @@
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
"tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="],
"tinyexec": ["tinyexec@1.1.2", "", {}, "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA=="],
"tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
@@ -1295,9 +1294,9 @@
"tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="],
"tldts": ["tldts@7.0.28", "", { "dependencies": { "tldts-core": "^7.0.28" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw=="],
"tldts": ["tldts@7.0.30", "", { "dependencies": { "tldts-core": "^7.0.30" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw=="],
"tldts-core": ["tldts-core@7.0.28", "", {}, "sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ=="],
"tldts-core": ["tldts-core@7.0.30", "", {}, "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
@@ -1315,7 +1314,7 @@
"typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="],
"ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
"ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="],
"undici": ["undici@7.25.0", "", {}, "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ=="],
@@ -1459,8 +1458,6 @@
"@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@tailwindcss/node/jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
@@ -1469,13 +1466,11 @@
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="],
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@tanstack/devtools-event-bus/ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="],
"@tanstack/router-generator/jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
"@tanstack/devtools-event-bus/ws": ["ws@8.20.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w=="],
"@tanstack/router-generator/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
@@ -1501,10 +1496,6 @@
"htmlparser2/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
"make-dir/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"nitro/h3": ["h3@2.0.1-rc.21", "", { "dependencies": { "rou3": "^0.8.1", "srvx": "^0.11.15" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"], "bin": { "h3": "bin/h3.mjs" } }, "sha512-lDeqAgCQXWT7C+5Zs3ler2phZPeX5yTk9KqQuL8taSSngIhcPR0r83TZyYwTO/cLogm6a4+9slZcngrfdyZtrQ=="],
"parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
"parse5-htmlparser2-tree-adapter/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
@@ -1515,7 +1506,7 @@
"readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.17", "", {}, "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg=="],
"rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
"strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
+333
View File
@@ -0,0 +1,333 @@
import { describe, expect, it } from "vitest";
import type { Id } from "./_generated/dataModel";
import {
currentUserSeedPackageName,
currentUserSeedSkillSlug,
seedFeaturedPluginPackagesMutation,
seedLocalModerationFixturesHandler,
seedSkillMutation,
} from "./devSeed";
type WrappedHandler<TArgs> = {
_handler: (ctx: unknown, args: TArgs) => Promise<unknown>;
};
const seedSkillMutationHandler = (
seedSkillMutation as unknown as WrappedHandler<Record<string, unknown>>
)._handler;
const seedFeaturedPluginPackagesHandler = (
seedFeaturedPluginPackagesMutation as unknown as WrappedHandler<Record<string, unknown>>
)._handler;
function chainEq(constraints: Record<string, unknown>) {
return {
eq(field: string, value: unknown) {
constraints[field] = value;
return chainEq(constraints);
},
};
}
function matches(doc: Record<string, unknown>, constraints: Record<string, unknown>) {
return Object.entries(constraints).every(([key, value]) => doc[key] === value);
}
function createDb() {
const tables: Record<string, Array<Record<string, unknown> & { _id: string }>> = {};
const counters: Record<string, number> = {};
const operations: Array<{ type: "delete"; table: string; id: string }> = [];
const list = (table: string) => {
tables[table] ??= [];
return tables[table];
};
const db = {
get: async (arg0: string, arg1?: string) => {
const id = arg1 ?? arg0;
const table = id.split(":")[0] ?? "";
return list(table).find((doc) => doc._id === id) ?? null;
},
insert: async (table: string, doc: Record<string, unknown>) => {
counters[table] = (counters[table] ?? 0) + 1;
const inserted = {
_id: `${table}:${counters[table]}`,
_creationTime: counters[table],
...doc,
};
list(table).push(inserted);
return inserted._id;
},
patch: async (
arg0: string,
arg1: string | Record<string, unknown>,
arg2?: Record<string, unknown>,
) => {
const id = arg2 ? (arg1 as string) : arg0;
const patch = arg2 ?? (arg1 as Record<string, unknown>);
const table = id.split(":")[0] ?? "";
const doc = list(table).find((candidate) => candidate._id === id);
if (doc) Object.assign(doc, patch);
},
replace: async (
arg0: string,
arg1: string | Record<string, unknown>,
arg2?: Record<string, unknown>,
) => {
const id = arg2 ? (arg1 as string) : arg0;
const replacement = arg2 ?? (arg1 as Record<string, unknown>);
const table = id.split(":")[0] ?? "";
const rows = list(table);
const index = rows.findIndex((doc) => doc._id === id);
if (index !== -1) rows[index] = { ...rows[index], ...replacement, _id: id };
},
delete: async (arg0: string, arg1?: string) => {
const id = arg1 ?? arg0;
const table = id.split(":")[0] ?? "";
operations.push({ type: "delete", table, id });
const rows = list(table);
const index = rows.findIndex((doc) => doc._id === id);
if (index !== -1) rows.splice(index, 1);
},
normalizeId: (tableName: string, id: string) => (id.startsWith(`${tableName}:`) ? id : null),
query: (table: string) => ({
withIndex: (_name: string, build: (q: ReturnType<typeof chainEq>) => unknown) => {
const constraints: Record<string, unknown> = {};
build(chainEq(constraints));
const matched = () =>
list(table).filter((doc) => matches(doc as Record<string, unknown>, constraints));
return {
collect: async () => matched(),
unique: async () => matched()[0] ?? null,
paginate: async () => ({
page: matched(),
isDone: true,
continueCursor: null,
}),
order: () => ({
collect: async () => matched(),
paginate: async () => ({
page: matched(),
isDone: true,
continueCursor: null,
}),
}),
};
},
}),
};
return { db, tables, operations };
}
function createMutationCtx(db: ReturnType<typeof createDb>["db"]) {
return { db, scheduler: { runAfter: async () => null } };
}
function seedSkillArgs(storageId: string) {
const clawdis = {
os: ["linux"],
nix: {
plugin: "github:example/catalog-demo",
systems: ["x86_64-linux"],
},
};
return {
storageId,
metadata: { clawdbot: { nix: clawdis.nix } },
frontmatter: { name: "catalog-demo", description: "Catalog demo" },
clawdis,
skillMd: "# Catalog demo",
slug: "catalog-demo",
displayName: "Catalog Demo",
summary: "Seeded catalog demo.",
version: "0.1.0",
};
}
describe("devSeed local fixtures", () => {
it("seeds core skill fixtures for an explicit local user without creating @local", async () => {
const { db, tables } = createDb();
const userId = (await db.insert("users", {
handle: "fuller-stack-dev",
displayName: "Fuller Stack Dev",
role: "user",
createdAt: 1,
updatedAt: 1,
})) as Id<"users">;
const scopedSlug = currentUserSeedSkillSlug(userId, "catalog-demo");
await seedSkillMutationHandler(
createMutationCtx(db) as never,
{
...seedSkillArgs("storage:first"),
ownerUserId: userId,
slug: scopedSlug,
} as never,
);
await seedSkillMutationHandler(
createMutationCtx(db) as never,
{
...seedSkillArgs("storage:second"),
ownerUserId: userId,
slug: scopedSlug,
} as never,
);
expect(tables.users).toHaveLength(1);
expect(tables.users?.[0]).toEqual(expect.objectContaining({ handle: "fuller-stack-dev" }));
expect(tables.publishers).toHaveLength(1);
expect(tables.publishers?.[0]).toEqual(
expect.objectContaining({ handle: "fuller-stack-dev", linkedUserId: userId }),
);
expect(tables.skills).toHaveLength(1);
expect(tables.skills?.[0]).toEqual(
expect.objectContaining({
slug: scopedSlug,
ownerUserId: userId,
ownerPublisherId: tables.publishers?.[0]?._id,
}),
);
});
it("seeds moderation and plugin fixtures for an explicit local user with scoped identifiers", async () => {
const { db, tables } = createDb();
const userId = (await db.insert("users", {
handle: "fuller-stack-dev",
displayName: "Fuller Stack Dev",
role: "user",
createdAt: 1,
updatedAt: 1,
})) as Id<"users">;
const flaggedSkillSlug = currentUserSeedSkillSlug(userId, "local-flagged-wallet-sync");
const scannedSkillSlug = currentUserSeedSkillSlug(userId, "local-agentic-risk-demo");
const flaggedPluginName = currentUserSeedPackageName(userId, "local-flagged-runtime-plugin");
const scannedPluginName = currentUserSeedPackageName(userId, "local-scanned-runtime-plugin");
await seedLocalModerationFixturesHandler(
createMutationCtx(db) as never,
{
ownerUserId: userId,
flaggedSkillSlug,
scannedSkillSlug,
flaggedPluginName,
scannedPluginName,
flaggedSkillStorageId: "storage:skill",
flaggedSkillMd: `---\nname: ${flaggedSkillSlug}\n---\n# Flagged skill`,
scannedSkillStorageId: "storage:scanned-skill",
scannedSkillMd: `---\nname: ${scannedSkillSlug}\n---\n# Scanned skill`,
flaggedPluginStorageId: "storage:plugin",
flaggedPluginReadme: "# Flagged plugin",
scannedPluginStorageId: "storage:scanned-plugin",
scannedPluginReadme: "# Scanned plugin",
} as never,
);
await seedFeaturedPluginPackagesHandler(
createMutationCtx(db) as never,
{
ownerUserId: userId,
packages: [
{
name: currentUserSeedPackageName(userId, "local-merge-notes-plugin"),
displayName: "Local Merge Notes",
summary: "Seeded local owner plugin.",
version: "0.1.0",
runtimeId: "local.merge.notes",
sourceRepo: "openclaw/local-merge-notes-plugin",
isOfficial: false,
capabilityTags: ["notes"],
stats: { downloads: 1, installs: 1, stars: 1, versions: 1 },
storageId: "storage:plugin-notes",
readmeSize: 16,
},
],
} as never,
);
expect(tables.users).toHaveLength(1);
expect(tables.users?.[0]).toEqual(expect.objectContaining({ handle: "fuller-stack-dev" }));
expect(
tables.skills?.map((skill) => String(skill.slug)).sort((a, b) => a.localeCompare(b)),
).toEqual([scannedSkillSlug, flaggedSkillSlug]);
expect(tables.skills?.every((skill) => skill.ownerUserId === userId)).toBe(true);
expect(
tables.packages?.map((pkg) => String(pkg.name)).sort((a, b) => a.localeCompare(b)),
).toEqual([
flaggedPluginName,
currentUserSeedPackageName(userId, "local-merge-notes-plugin"),
scannedPluginName,
]);
expect(tables.packages?.every((pkg) => pkg.ownerUserId === userId)).toBe(true);
});
it("resets core skill fixtures without stale badges or embedding maps", async () => {
const { db, tables } = createDb();
await seedSkillMutationHandler(
createMutationCtx(db) as never,
seedSkillArgs("storage:first") as never,
);
await seedSkillMutationHandler(
createMutationCtx(db) as never,
{ ...seedSkillArgs("storage:second"), reset: true } as never,
);
expect(tables.skills).toHaveLength(1);
expect(tables.skillVersions).toHaveLength(1);
expect(tables.skillEmbeddings).toHaveLength(1);
expect(tables.embeddingSkillMap).toHaveLength(1);
expect(tables.skillBadges).toHaveLength(1);
expect(tables.skills?.[0]?.latestVersionSummary).toBeUndefined();
expect(tables.skillVersions?.[0]).toEqual(
expect.objectContaining({
parsed: expect.objectContaining({
clawdis: expect.objectContaining({
os: ["linux"],
nix: expect.objectContaining({ systems: ["x86_64-linux"] }),
}),
}),
}),
);
});
it("resets featured plugin fixtures without stale package badges", async () => {
const { db, tables, operations } = createDb();
const args = {
packages: [
{
name: "@local/catalog-plugin",
displayName: "Catalog Plugin",
summary: "Seeded catalog plugin.",
version: "1.0.0",
runtimeId: "catalog-plugin",
sourceRepo: "openclaw/catalog-plugin",
isOfficial: false,
capabilityTags: ["catalog"],
stats: { downloads: 1, installs: 1, stars: 1, versions: 1 },
storageId: "storage:plugin",
readmeSize: 16,
},
],
};
await seedFeaturedPluginPackagesHandler(createMutationCtx(db) as never, args as never);
const oldPackageId = tables.packages?.[0]?._id;
const oldReleaseId = tables.packageReleases?.[0]?._id;
await seedFeaturedPluginPackagesHandler(
createMutationCtx(db) as never,
{ ...args, reset: true } as never,
);
expect(tables.packages).toHaveLength(1);
expect(tables.packageReleases).toHaveLength(1);
expect(tables.packageBadges).toHaveLength(1);
const oldPackageDeleteIndex = operations.findIndex(
(op) => op.table === "packages" && op.id === oldPackageId,
);
const oldReleaseDeleteIndex = operations.findIndex(
(op) => op.table === "packageReleases" && op.id === oldReleaseId,
);
expect(oldPackageDeleteIndex).toBeGreaterThanOrEqual(0);
expect(oldReleaseDeleteIndex).toBeGreaterThan(oldPackageDeleteIndex);
});
});
+237 -38
View File
@@ -34,6 +34,7 @@ type SeedPluginSpec = {
type SeedActionArgs = {
reset?: boolean;
ownerUserId?: Id<"users">;
};
type SeedActionResult = {
@@ -45,6 +46,7 @@ type SeedMutationResult = Record<string, unknown>;
const LOCAL_SEED_HANDLE = "local";
const LOCAL_SEED_GITHUB_CREATED_AT = Date.parse("2020-01-01T00:00:00.000Z");
const CURRENT_USER_SEED_PREFIX = "dev";
const FLAGGED_SKILL_SLUG = "local-flagged-wallet-sync";
const SCANNED_SKILL_SLUG = "local-agentic-risk-demo";
const FLAGGED_PLUGIN_NAME = "local-flagged-runtime-plugin";
@@ -169,6 +171,52 @@ This seeded plugin is public and intentionally has completed scan results so loc
preview plugin scanner detail pages without owner-only visibility.
`;
function isProductionDeployment() {
const deployment = process.env.CONVEX_DEPLOYMENT?.trim() ?? "";
return deployment.startsWith("prod:") || deployment.includes("production");
}
function assertDevSeedAllowed() {
if (isProductionDeployment()) {
throw new Error("Dev fixture seeding is disabled on production deployments.");
}
}
function normalizeSeedPart(value: string) {
return value
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "")
.replace(/-{2,}/g, "-");
}
function currentUserSeedKey(userId: Id<"users">) {
const raw = String(userId).split(":").pop() ?? String(userId);
const normalized = normalizeSeedPart(raw).replace(/-/g, "");
return (normalized || "user").slice(-8);
}
export function currentUserSeedSkillSlug(userId: Id<"users">, baseSlug: string) {
return `${CURRENT_USER_SEED_PREFIX}-${currentUserSeedKey(userId)}-${baseSlug}`;
}
export function currentUserSeedPackageName(userId: Id<"users">, baseName: string) {
const normalized = normalizePackageName(baseName).replace(/^@/, "").replace("/", "-");
return `${CURRENT_USER_SEED_PREFIX}-${currentUserSeedKey(userId)}-${normalized}`;
}
function withFrontmatterName(rawSkillMd: string, name: string) {
const frontmatterEnd = rawSkillMd.indexOf("\n---", 3);
if (frontmatterEnd === -1) return rawSkillMd;
const frontmatter = rawSkillMd.slice(0, frontmatterEnd);
const body = rawSkillMd.slice(frontmatterEnd);
if (/^name:\s*.+$/m.test(frontmatter)) {
return `${frontmatter.replace(/^name:\s*.+$/m, `name: ${name}`)}${body}`;
}
return `${frontmatter}\nname: ${name}${body}`;
}
const FEATURED_PLUGIN_SEEDS: SeedPluginSpec[] = [
{
name: "@apify/apify-openclaw-plugin",
@@ -700,6 +748,7 @@ async function seedPluginPackageBatch(
);
return (await ctx.runMutation(internal.devSeed.seedFeaturedPluginPackagesMutation, {
reset: args.reset,
ownerUserId: args.ownerUserId,
packages: specs.map((spec, index) => ({
name: spec.name,
displayName: spec.displayName,
@@ -786,6 +835,129 @@ export const seedNixSkills: ReturnType<typeof internalAction> = internalAction({
handler: seedNixSkillsHandler,
});
type SeedCurrentUserFixturesArgs = {
reset?: boolean;
ownerUserId: Id<"users">;
};
async function seedCurrentUserFixturesHandler(ctx: ActionCtx, args: SeedCurrentUserFixturesArgs) {
assertDevSeedAllowed();
const userId = args.ownerUserId;
const skillSlugs: string[] = [];
const packageNames: string[] = [];
const results: Array<Record<string, unknown> & { slug: string }> = [];
for (const spec of SEED_SKILLS) {
const slug = currentUserSeedSkillSlug(userId, spec.slug);
const skillMd = withFrontmatterName(injectMetadata(spec.rawSkillMd, spec.metadata), slug);
const frontmatter = parseFrontmatter(skillMd);
const clawdis = parseClawdisMetadata(frontmatter);
const storageId = await ctx.storage.store(new Blob([skillMd], { type: "text/markdown" }));
const result: SeedMutationResult = await ctx.runMutation(internal.devSeed.seedSkillMutation, {
reset: args.reset,
ownerUserId: userId,
storageId,
metadata: spec.metadata,
frontmatter,
clawdis,
skillMd,
slug,
displayName: spec.displayName,
summary: spec.summary,
version: spec.version,
});
skillSlugs.push(slug);
results.push({ slug, ...result });
}
const flaggedSkillSlug = currentUserSeedSkillSlug(userId, FLAGGED_SKILL_SLUG);
const scannedSkillSlug = currentUserSeedSkillSlug(userId, SCANNED_SKILL_SLUG);
const flaggedPluginName = currentUserSeedPackageName(userId, FLAGGED_PLUGIN_NAME);
const scannedPluginName = currentUserSeedPackageName(userId, SCANNED_PLUGIN_NAME);
const flaggedSkillMd = withFrontmatterName(FLAGGED_SKILL_MD, flaggedSkillSlug);
const scannedSkillMd = withFrontmatterName(SCANNED_SKILL_MD, scannedSkillSlug);
const flaggedPluginReadme = FLAGGED_PLUGIN_README.replace(
"# Local Flagged Runtime Plugin",
`# ${flaggedPluginName}`,
);
const scannedPluginReadme = SCANNED_PLUGIN_README.replace(
"# Local Scanned Runtime Plugin",
`# ${scannedPluginName}`,
);
const [
flaggedSkillStorageId,
scannedSkillStorageId,
flaggedPluginStorageId,
scannedPluginStorageId,
] = await Promise.all([
ctx.storage.store(new Blob([flaggedSkillMd], { type: "text/markdown" })),
ctx.storage.store(new Blob([scannedSkillMd], { type: "text/markdown" })),
ctx.storage.store(new Blob([flaggedPluginReadme], { type: "text/markdown" })),
ctx.storage.store(new Blob([scannedPluginReadme], { type: "text/markdown" })),
]);
const fixtureResult: SeedMutationResult = await ctx.runMutation(
internal.devSeed.seedLocalModerationFixturesMutation,
{
reset: args.reset,
ownerUserId: userId,
flaggedSkillSlug,
scannedSkillSlug,
flaggedPluginName,
scannedPluginName,
flaggedSkillStorageId,
flaggedSkillMd,
scannedSkillStorageId,
scannedSkillMd,
flaggedPluginStorageId,
flaggedPluginReadme,
scannedPluginStorageId,
scannedPluginReadme,
},
);
skillSlugs.push(flaggedSkillSlug, scannedSkillSlug);
packageNames.push(flaggedPluginName, scannedPluginName);
results.push({ slug: flaggedSkillSlug, ...fixtureResult });
const ownerPluginSpecs = LOCAL_OWNER_PLUGIN_SEEDS.map((spec) => ({
...spec,
name: currentUserSeedPackageName(userId, spec.name),
runtimeId: `${CURRENT_USER_SEED_PREFIX}.${currentUserSeedKey(userId)}.${spec.runtimeId}`,
}));
const ownerPluginResult = await seedPluginPackageBatch(
ctx,
{ reset: args.reset, ownerUserId: userId },
ownerPluginSpecs,
);
packageNames.push(...ownerPluginSpecs.map((spec) => spec.name));
results.push({ slug: "current-user-local-plugins", ...ownerPluginResult });
const statsResult = (await ctx.runAction(
internal.statsMaintenance.updateGlobalStatsAction,
{},
)) as { count: number } | null;
return {
ok: true,
ownerUserId: userId,
skillCount: skillSlugs.length,
pluginCount: packageNames.length,
skillSlugs,
packageNames,
statsCount: statsResult?.count ?? null,
results,
};
}
export const seedCurrentUserFixtures: ReturnType<typeof internalAction> = internalAction({
args: {
reset: v.optional(v.boolean()),
ownerUserId: v.id("users"),
},
handler: seedCurrentUserFixturesHandler,
});
async function seedPadelSkillHandler(
ctx: ActionCtx,
args: SeedActionArgs,
@@ -851,6 +1023,17 @@ async function ensureLocalSeedOwner(ctx: MutationCtx) {
return { userId: ensuredUserId, publisherId: publisher._id };
}
async function ensureSeedOwner(ctx: MutationCtx, ownerUserId?: Id<"users">) {
if (!ownerUserId) return await ensureLocalSeedOwner(ctx);
const user = await ctx.db.get(ownerUserId);
if (!user || user.deletedAt || user.deactivatedAt) {
throw new Error("Seed owner user not found");
}
const publisher = await ensurePersonalPublisherForUser(ctx, user);
if (!publisher) throw new Error("Seed owner publisher was not created");
return { userId: user._id, publisherId: publisher._id };
}
async function deleteSkillEmbeddingsForSkill(ctx: MutationCtx, skillId: Id<"skills">) {
const embeddings = await ctx.db
.query("skillEmbeddings")
@@ -882,8 +1065,8 @@ async function deletePackageBadgesForPackage(ctx: MutationCtx, packageId: Id<"pa
for (const badge of badges) await ctx.db.delete(badge._id);
}
async function deleteSeedSkillFixture(ctx: MutationCtx) {
const existing = await findSeedSkillFixture(ctx);
async function deleteSeedSkillFixture(ctx: MutationCtx, slug = FLAGGED_SKILL_SLUG) {
const existing = await findSeedSkillFixture(ctx, slug);
if (!existing) return;
const versions = await ctx.db
@@ -909,15 +1092,15 @@ async function deleteSeedSkillFixture(ctx: MutationCtx) {
await ctx.db.delete(existing._id);
}
async function findSeedSkillFixture(ctx: MutationCtx) {
async function findSeedSkillFixture(ctx: MutationCtx, slug = FLAGGED_SKILL_SLUG) {
return await ctx.db
.query("skills")
.withIndex("by_slug", (q) => q.eq("slug", FLAGGED_SKILL_SLUG))
.withIndex("by_slug", (q) => q.eq("slug", slug))
.unique();
}
async function deleteScannedSkillFixture(ctx: MutationCtx) {
const existing = await findScannedSkillFixture(ctx);
async function deleteScannedSkillFixture(ctx: MutationCtx, slug = SCANNED_SKILL_SLUG) {
const existing = await findScannedSkillFixture(ctx, slug);
if (!existing) return;
const versions = await ctx.db
@@ -943,10 +1126,10 @@ async function deleteScannedSkillFixture(ctx: MutationCtx) {
await ctx.db.delete(existing._id);
}
async function findScannedSkillFixture(ctx: MutationCtx) {
async function findScannedSkillFixture(ctx: MutationCtx, slug = SCANNED_SKILL_SLUG) {
return await ctx.db
.query("skills")
.withIndex("by_slug", (q) => q.eq("slug", SCANNED_SKILL_SLUG))
.withIndex("by_slug", (q) => q.eq("slug", slug))
.unique();
}
@@ -965,12 +1148,12 @@ async function deleteSeedPluginFixtureByName(ctx: MutationCtx, name: string) {
}
}
async function deleteSeedPluginFixture(ctx: MutationCtx) {
await deleteSeedPluginFixtureByName(ctx, FLAGGED_PLUGIN_NAME);
async function deleteSeedPluginFixture(ctx: MutationCtx, name = FLAGGED_PLUGIN_NAME) {
await deleteSeedPluginFixtureByName(ctx, name);
}
async function deleteScannedPluginFixture(ctx: MutationCtx) {
await deleteSeedPluginFixtureByName(ctx, SCANNED_PLUGIN_NAME);
async function deleteScannedPluginFixture(ctx: MutationCtx, name = SCANNED_PLUGIN_NAME) {
await deleteSeedPluginFixtureByName(ctx, name);
}
async function findSeedPluginFixtureByName(ctx: MutationCtx, name: string) {
@@ -980,12 +1163,12 @@ async function findSeedPluginFixtureByName(ctx: MutationCtx, name: string) {
.unique();
}
async function findSeedPluginFixture(ctx: MutationCtx) {
return await findSeedPluginFixtureByName(ctx, FLAGGED_PLUGIN_NAME);
async function findSeedPluginFixture(ctx: MutationCtx, name = FLAGGED_PLUGIN_NAME) {
return await findSeedPluginFixtureByName(ctx, name);
}
async function findScannedPluginFixture(ctx: MutationCtx) {
return await findSeedPluginFixtureByName(ctx, SCANNED_PLUGIN_NAME);
async function findScannedPluginFixture(ctx: MutationCtx, name = SCANNED_PLUGIN_NAME) {
return await findSeedPluginFixtureByName(ctx, name);
}
async function ensureSkillBadge(
@@ -1433,6 +1616,11 @@ function flaggedWalletClawScanAnalysis(now: number) {
type SeedLocalModerationFixturesArgs = {
reset?: boolean;
ownerUserId?: Id<"users">;
flaggedSkillSlug?: string;
scannedSkillSlug?: string;
flaggedPluginName?: string;
scannedPluginName?: string;
flaggedSkillStorageId: Id<"_storage">;
flaggedSkillMd: string;
scannedSkillStorageId: Id<"_storage">;
@@ -1449,10 +1637,14 @@ export async function seedLocalModerationFixturesHandler(
) {
const scannedSkillFrontmatter = parseFrontmatter(args.scannedSkillMd);
const scannedSkillClawdis = parseClawdisMetadata(scannedSkillFrontmatter);
const existingSkill = await findSeedSkillFixture(ctx);
const existingScannedSkill = await findScannedSkillFixture(ctx);
const existingPlugin = await findSeedPluginFixture(ctx);
const existingScannedPlugin = await findScannedPluginFixture(ctx);
const flaggedSkillSlug = args.flaggedSkillSlug ?? FLAGGED_SKILL_SLUG;
const scannedSkillSlug = args.scannedSkillSlug ?? SCANNED_SKILL_SLUG;
const flaggedPluginName = args.flaggedPluginName ?? FLAGGED_PLUGIN_NAME;
const scannedPluginName = args.scannedPluginName ?? SCANNED_PLUGIN_NAME;
const existingSkill = await findSeedSkillFixture(ctx, flaggedSkillSlug);
const existingScannedSkill = await findScannedSkillFixture(ctx, scannedSkillSlug);
const existingPlugin = await findSeedPluginFixture(ctx, flaggedPluginName);
const existingScannedPlugin = await findScannedPluginFixture(ctx, scannedPluginName);
if (
existingSkill &&
existingScannedSkill &&
@@ -1461,7 +1653,7 @@ export async function seedLocalModerationFixturesHandler(
!args.reset
) {
const now = Date.now();
const { userId, publisherId } = await ensureLocalSeedOwner(ctx);
const { userId, publisherId } = await ensureSeedOwner(ctx, args.ownerUserId);
const ownerPatch = { ownerUserId: userId, ownerPublisherId: publisherId, updatedAt: now };
for (const skill of [existingSkill, existingScannedSkill]) {
if (skill.ownerUserId !== userId || skill.ownerPublisherId !== publisherId) {
@@ -1497,7 +1689,7 @@ export async function seedLocalModerationFixturesHandler(
],
parsed: {
frontmatter: {
name: FLAGGED_SKILL_SLUG,
name: flaggedSkillSlug,
description:
"Reconcile local wallet exports against exchange activity and flag mismatched transfers.",
},
@@ -1561,19 +1753,19 @@ export async function seedLocalModerationFixturesHandler(
};
}
await deleteSeedSkillFixture(ctx);
await deleteScannedSkillFixture(ctx);
await deleteSeedPluginFixture(ctx);
await deleteScannedPluginFixture(ctx);
await deleteSeedSkillFixture(ctx, flaggedSkillSlug);
await deleteScannedSkillFixture(ctx, scannedSkillSlug);
await deleteSeedPluginFixture(ctx, flaggedPluginName);
await deleteScannedPluginFixture(ctx, scannedPluginName);
const now = Date.now();
const { userId, publisherId } = await ensureLocalSeedOwner(ctx);
const { userId, publisherId } = await ensureSeedOwner(ctx, args.ownerUserId);
const staticScan = staticMaliciousScan(now);
const scannedSkillStaticScan = staticSuspiciousSkillScan(now);
const scannedStaticScan = staticSuspiciousScan(now);
const skillId = await ctx.db.insert("skills", {
slug: FLAGGED_SKILL_SLUG,
slug: flaggedSkillSlug,
displayName: "Local Flagged Wallet Sync",
summary:
"Reconcile local wallet exports against exchange activity and flag mismatched transfers.",
@@ -1626,7 +1818,7 @@ export async function seedLocalModerationFixturesHandler(
],
parsed: {
frontmatter: {
name: FLAGGED_SKILL_SLUG,
name: flaggedSkillSlug,
description:
"Reconcile local wallet exports against exchange activity and flag mismatched transfers.",
},
@@ -1660,7 +1852,7 @@ export async function seedLocalModerationFixturesHandler(
updatedAt: now,
});
const scannedSkillId = await ctx.db.insert("skills", {
slug: SCANNED_SKILL_SLUG,
slug: scannedSkillSlug,
displayName: "Local Agentic Risk Demo",
summary: SCANNED_SKILL_SUMMARY,
ownerUserId: userId,
@@ -1758,8 +1950,8 @@ export async function seedLocalModerationFixturesHandler(
});
const packageId = await ctx.db.insert("packages", {
name: FLAGGED_PLUGIN_NAME,
normalizedName: normalizePackageName(FLAGGED_PLUGIN_NAME),
name: flaggedPluginName,
normalizedName: normalizePackageName(flaggedPluginName),
displayName: "Local Flagged Runtime Plugin",
summary: "Seeded flagged plugin for local owner inventory and security review testing.",
ownerUserId: userId,
@@ -1811,7 +2003,7 @@ export async function seedLocalModerationFixturesHandler(
],
integritySha256: "seeded-flagged-plugin-integrity",
extractedPackageJson: {
name: FLAGGED_PLUGIN_NAME,
name: flaggedPluginName,
version: "0.1.0",
},
compatibility: { pluginApiRange: ">=0.1.0" },
@@ -1877,8 +2069,8 @@ export async function seedLocalModerationFixturesHandler(
updatedAt: now,
});
const scannedPackageId = await ctx.db.insert("packages", {
name: SCANNED_PLUGIN_NAME,
normalizedName: normalizePackageName(SCANNED_PLUGIN_NAME),
name: scannedPluginName,
normalizedName: normalizePackageName(scannedPluginName),
displayName: "Local Scanned Runtime Plugin",
summary: "Seeded public plugin with completed security scans for scanner page previews.",
ownerUserId: userId,
@@ -1930,7 +2122,7 @@ export async function seedLocalModerationFixturesHandler(
],
integritySha256: "seeded-scanned-plugin-integrity",
extractedPackageJson: {
name: SCANNED_PLUGIN_NAME,
name: scannedPluginName,
version: "0.1.0",
},
compatibility: { pluginApiRange: ">=0.1.0" },
@@ -2014,6 +2206,11 @@ export async function seedLocalModerationFixturesHandler(
export const seedLocalModerationFixturesMutation = internalMutation({
args: {
reset: v.optional(v.boolean()),
ownerUserId: v.optional(v.id("users")),
flaggedSkillSlug: v.optional(v.string()),
scannedSkillSlug: v.optional(v.string()),
flaggedPluginName: v.optional(v.string()),
scannedPluginName: v.optional(v.string()),
flaggedSkillStorageId: v.id("_storage"),
flaggedSkillMd: v.string(),
scannedSkillStorageId: v.id("_storage"),
@@ -2029,6 +2226,7 @@ export const seedLocalModerationFixturesMutation = internalMutation({
export const seedFeaturedPluginPackagesMutation = internalMutation({
args: {
reset: v.optional(v.boolean()),
ownerUserId: v.optional(v.id("users")),
packages: v.array(
v.object({
name: v.string(),
@@ -2052,7 +2250,7 @@ export const seedFeaturedPluginPackagesMutation = internalMutation({
},
handler: async (ctx, args) => {
const now = Date.now();
const { userId, publisherId } = await ensureLocalSeedOwner(ctx);
const { userId, publisherId } = await ensureSeedOwner(ctx, args.ownerUserId);
const seeded: string[] = [];
const skipped: string[] = [];
@@ -2412,6 +2610,7 @@ async function replaceRoleHelpFixtureToken(ctx: MutationCtx, userId: Id<"users">
export const seedSkillMutation = internalMutation({
args: {
reset: v.optional(v.boolean()),
ownerUserId: v.optional(v.id("users")),
storageId: v.id("_storage"),
metadata: v.any(),
frontmatter: v.any(),
@@ -2424,7 +2623,7 @@ export const seedSkillMutation = internalMutation({
},
handler: async (ctx, args) => {
const now = Date.now();
const { userId, publisherId } = await ensureLocalSeedOwner(ctx);
const { userId, publisherId } = await ensureSeedOwner(ctx, args.ownerUserId);
const existing = await ctx.db
.query("skills")
.withIndex("by_slug", (q) => q.eq("slug", args.slug))
+2 -3
View File
@@ -835,12 +835,11 @@ async function getSkillSlugAliasBySlugForAvailabilityPublisher(
if (scopedAliases[0]) return scopedAliases[0];
if (publisher.kind !== "user" || !publisher.linkedUserId) return null;
const linkedUserId = publisher.linkedUserId;
const legacyAliases = await ctx.db
.query("skillSlugAliases")
.withIndex("by_owner_slug", (q) =>
q.eq("ownerUserId", publisher.linkedUserId as Id<"users">).eq("slug", slug),
)
.withIndex("by_owner_slug", (q) => q.eq("ownerUserId", linkedUserId).eq("slug", slug))
.take(2);
return (
legacyAliases.find(
+57 -55
View File
@@ -2,7 +2,9 @@
"name": "clawhub",
"private": true,
"workspaces": [
"packages/*"
"packages/clawhub",
"packages/clawhub-mod",
"packages/schema"
],
"type": "module",
"scripts": {
@@ -63,76 +65,76 @@
"verify:convex-contract": "bun scripts/verify-convex-contract.ts"
},
"dependencies": {
"@auth/core": "^0.37.4",
"@auth/core": "0.37.4",
"@convex-dev/auth": "0.0.92",
"@fontsource/bricolage-grotesque": "^5.2.10",
"@fontsource/ibm-plex-mono": "^5.2.7",
"@fontsource/manrope": "^5.2.8",
"@monaco-editor/react": "^4.7.0",
"@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"@resvg/resvg-wasm": "^2.6.2",
"@shikijs/rehype": "^4.0.2",
"@fontsource/bricolage-grotesque": "5.2.10",
"@fontsource/ibm-plex-mono": "5.2.7",
"@fontsource/manrope": "5.2.8",
"@monaco-editor/react": "4.7.0",
"@radix-ui/react-avatar": "1.1.11",
"@radix-ui/react-dialog": "1.1.15",
"@radix-ui/react-dropdown-menu": "2.1.16",
"@radix-ui/react-label": "2.1.8",
"@radix-ui/react-select": "2.2.6",
"@radix-ui/react-separator": "1.1.8",
"@radix-ui/react-slot": "1.2.4",
"@radix-ui/react-toggle-group": "1.1.11",
"@radix-ui/react-tooltip": "1.2.8",
"@resvg/resvg-wasm": "2.6.2",
"@shikijs/rehype": "4.0.2",
"@tanstack/react-router": "1.169.2",
"@tanstack/react-start": "1.167.65",
"@vercel/analytics": "^2.0.1",
"class-variance-authority": "^0.7.1",
"clawhub-schema": "workspace:*",
"clsx": "^2.1.1",
"convex": "^1.38.0",
"convex-helpers": "^0.1.116",
"fflate": "^0.8.2",
"@vercel/analytics": "2.0.1",
"class-variance-authority": "0.7.1",
"clawhub-schema": "workspace:0.0.2",
"clsx": "2.1.1",
"convex": "1.38.0",
"convex-helpers": "0.1.116",
"fflate": "0.8.2",
"h3": "2.0.1-rc.22",
"ignore": "^7.0.5",
"ignore": "7.0.5",
"lucide-react": "1.14.0",
"mime": "^4.1.0",
"monaco-editor": "^0.55.1",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"react-markdown": "^10.1.0",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.1",
"semver": "^7.8.0",
"shiki": "^4.0.2",
"sonner": "^2.0.7",
"tailwind-merge": "^3.6.0",
"tailwindcss": "^4.3.0",
"tw-animate-css": "^1.4.0",
"unified": "^11.0.5",
"unist-util-visit": "^5.1.0",
"yaml": "^2.9.0",
"mime": "4.1.0",
"monaco-editor": "0.55.1",
"react": "19.2.6",
"react-dom": "19.2.6",
"react-markdown": "10.1.0",
"rehype-raw": "7.0.0",
"rehype-sanitize": "6.0.0",
"remark-gfm": "4.0.1",
"semver": "7.8.0",
"shiki": "4.0.2",
"sonner": "2.0.7",
"tailwind-merge": "3.6.0",
"tailwindcss": "4.3.0",
"tw-animate-css": "1.4.0",
"unified": "11.0.5",
"unist-util-visit": "5.1.0",
"yaml": "2.9.0",
"zod": "4.4.3"
},
"devDependencies": {
"@playwright/test": "^1.60.0",
"@tailwindcss/vite": "^4.3.0",
"@playwright/test": "1.60.0",
"@tailwindcss/vite": "4.3.0",
"@tanstack/devtools-vite": "0.6.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.2",
"@types/node": "^25.7.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@types/semver": "^7.7.1",
"@testing-library/dom": "10.4.1",
"@testing-library/react": "16.3.2",
"@types/node": "25.7.0",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"@types/semver": "7.7.1",
"@vitejs/plugin-react": "6.0.1",
"@vitest/coverage-v8": "^4.1.6",
"jsdom": "^29.1.1",
"@vitest/coverage-v8": "4.1.6",
"jsdom": "29.1.1",
"nitro": "3.0.260429-beta",
"only-allow": "^1.2.2",
"only-allow": "1.2.2",
"oxfmt": "0.49.0",
"oxlint": "^1.64.0",
"oxlint": "1.64.0",
"oxlint-tsgolint": "0.22.1",
"typescript": "6.0.3",
"undici": "7.25.0",
"vite": "8.0.12",
"vitest": "^4.1.6"
"vitest": "4.1.6"
},
"overrides": {
"dompurify": "3.4.1",
+10 -10
View File
@@ -27,20 +27,20 @@
"verify": "bun run test && bun run typecheck && bun run build"
},
"dependencies": {
"@clack/prompts": "^1.4.0",
"arktype": "^2.2.0",
"commander": "^14.0.3",
"fflate": "^0.8.2",
"ignore": "^7.0.5",
"json5": "^2.2.3",
"mime": "^4.1.0",
"ora": "^9.4.0",
"@clack/prompts": "1.4.0",
"arktype": "2.2.0",
"commander": "14.0.3",
"fflate": "0.8.2",
"ignore": "7.0.5",
"json5": "2.2.3",
"mime": "4.1.0",
"ora": "9.4.0",
"p-retry": "8.0.0",
"semver": "^7.8.0",
"semver": "7.8.0",
"undici": "7.25.0"
},
"devDependencies": {
"@types/node": "^25.7.0",
"@types/node": "25.7.0",
"typescript": "6.0.3"
},
"engines": {
+10 -10
View File
@@ -37,20 +37,20 @@
"verify:build": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"@clack/prompts": "^1.4.0",
"arktype": "^2.2.0",
"commander": "^14.0.3",
"fflate": "^0.8.2",
"ignore": "^7.0.5",
"json5": "^2.2.3",
"mime": "^4.1.0",
"ora": "^9.4.0",
"@clack/prompts": "1.4.0",
"arktype": "2.2.0",
"commander": "14.0.3",
"fflate": "0.8.2",
"ignore": "7.0.5",
"json5": "2.2.3",
"mime": "4.1.0",
"ora": "9.4.0",
"p-retry": "8.0.0",
"semver": "^7.8.0",
"semver": "7.8.0",
"undici": "7.25.0"
},
"devDependencies": {
"@types/node": "^25.7.0",
"@types/node": "25.7.0",
"typescript": "6.0.3"
},
"engines": {
+1 -1
View File
@@ -30,7 +30,7 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
"arktype": "^2.2.0"
"arktype": "2.2.0"
},
"devDependencies": {
"typescript": "6.0.3"
+25
View File
@@ -0,0 +1,25 @@
---
summary: "Local development fixture seeding ownership rules."
read_when:
- Working on local seed data
- Editing dashboard empty states
- Changing devSeed fixtures
---
# Dev Seeding
Local fixture seeding is command-driven by default:
- CLI seeding (`bun run seed:dev`) populates shared catalog fixtures under `@local`, including
skill, plugin, scanner, and moderation fixtures. This is the documented local setup path.
- `internal.devSeed.seedCurrentUserFixtures` remains a dev-only internal action for explicit local
development tools/tests that need fixtures cloned to a local user.
Current-user fixture seeding must not be exposed as a public Convex `api` function or browser UI
action. Internal tooling may pass an `ownerUserId`, but that id must stay inside trusted local seed
tooling rather than crossing a frontend boundary. Fixture slugs and package names must include a
stable per-user seed key so multiple developers can use the same dev deployment without colliding.
Current-user fixture seeding is dev-only. It must reject production Convex deployments, and it
should not be exposed as a first-run dashboard button unless the UX and ownership rules are
intentionally revisited.
+57
View File
@@ -571,6 +571,63 @@ describe("SkillDetailPage", () => {
});
});
it("redirects merged source slugs to the resolved canonical slug", async () => {
useQueryMock.mockImplementation((_fn: unknown, args: unknown) => {
if (args === "skip") return undefined;
if (args && typeof args === "object" && "skillId" in args) return [];
if (args && typeof args === "object" && "slug" in args) {
return {
requestedSlug: "old-weather",
resolvedSlug: "weather",
skill: {
_id: "skills:1",
slug: "weather",
displayName: "Weather",
summary: "Get current weather.",
ownerUserId: "users:1",
ownerPublisherId: "publishers:steipete",
tags: {},
badges: {},
stats: {
stars: 0,
downloads: 0,
installsCurrent: 0,
installsAllTime: 0,
versions: 1,
comments: 0,
},
createdAt: 0,
updatedAt: 0,
},
owner: {
_id: "publishers:steipete",
_creationTime: 0,
kind: "user",
handle: "steipete",
displayName: "Peter",
linkedUserId: "users:1",
},
latestVersion: { _id: "skillVersions:1", version: "1.0.0", parsed: {}, files: [] },
forkOf: null,
canonical: null,
};
}
return undefined;
});
render(<SkillDetailPage slug="old-weather" canonicalOwner="steipete" />);
expect(screen.getByRole("status", { name: /Loading skill details/i })).toBeTruthy();
await waitFor(() => {
expect(navigateMock).toHaveBeenCalled();
});
expect(navigateMock).toHaveBeenCalledWith({
to: "/$owner/$slug",
params: { owner: "steipete", slug: "weather" },
replace: true,
});
});
it("does not redirect when a staff owner handle only differs by case", async () => {
useAuthStatusMock.mockReturnValue({
isAuthenticated: true,
+13 -3
View File
@@ -293,6 +293,7 @@ export function SkillDetailPage({
redirectToCanonical ||
(canonicalOwnerParam && canonicalOwnerParam !== ownerParam)),
);
const redirectSlug = result?.resolvedSlug ?? skill?.slug ?? slug;
const forkOf = result?.forkOf ?? null;
const canonical = result?.canonical ?? null;
@@ -364,13 +365,22 @@ export function SkillDetailPage({
const latestFiles: SkillFile[] = latestVersion?.files ?? [];
useEffect(() => {
if (!wantsCanonicalRedirect || !ownerParam) return;
if (!wantsCanonicalRedirect || !ownerParam || !redirectSlug) return;
const params = { owner: ownerParam, slug: redirectSlug };
if (mode === "settings") {
void navigate({
to: "/$owner/$slug/settings",
params,
replace: true,
});
return;
}
void navigate({
to: "/$owner/$slug",
params: { owner: ownerParam, slug },
params,
replace: true,
});
}, [navigate, ownerParam, slug, wantsCanonicalRedirect]);
}, [mode, navigate, ownerParam, redirectSlug, wantsCanonicalRedirect]);
useEffect(() => {
if (typeof window === "undefined") return undefined;
-15
View File
@@ -9,22 +9,11 @@ import { Dashboard } from "./dashboard";
const mocks = vi.hoisted(() => ({
useQuery: vi.fn(),
usePaginatedQuery: vi.fn(),
useMutation: vi.fn(),
toastSuccess: vi.fn(),
toastError: vi.fn(),
}));
vi.mock("convex/react", () => ({
useQuery: (...args: unknown[]) => mocks.useQuery(...args),
usePaginatedQuery: (...args: unknown[]) => mocks.usePaginatedQuery(...args),
useMutation: (...args: unknown[]) => mocks.useMutation(...args),
}));
vi.mock("sonner", () => ({
toast: {
success: (...args: unknown[]) => mocks.toastSuccess(...args),
error: (...args: unknown[]) => mocks.toastError(...args),
},
}));
vi.mock("@tanstack/react-router", () => ({
@@ -272,10 +261,6 @@ describe("Dashboard rows", () => {
status: "LoadingFirstPage",
loadMore: vi.fn(),
});
mocks.useMutation.mockReset();
mocks.useMutation.mockReturnValue(vi.fn().mockResolvedValue({}));
mocks.toastSuccess.mockReset();
mocks.toastError.mockReset();
});
it("renders compact clickable artifact cards with status and inventory context", () => {
+3 -1
View File
@@ -170,6 +170,8 @@ export function Dashboard() {
const isLoading = skillsStatus === "LoadingFirstPage";
const ownerHandle =
selectedPublisher?.publisher.handle ?? me.handle ?? me.name ?? me.displayName ?? me._id;
const isDashboardEmpty = !isLoading && skills.length === 0 && packages.length === 0;
const publisherSelector =
publishers && publishers.length > 1 ? (
<div className="dashboard-publisher-select">
@@ -193,7 +195,7 @@ export function Dashboard() {
) : null;
// Welcome state for new users with no content
if (!isLoading && skills.length === 0 && packages.length === 0) {
if (isDashboardEmpty) {
return (
<main className="section">
<div className="empty-state">