From a5ba7124dc8d021a2a46975176898c1dab0d2017 Mon Sep 17 00:00:00 2001 From: noelorin Date: Thu, 24 Sep 2026 15:21:45 +0800 Subject: [PATCH] chore: initial app template scaffold Astro + tRPC + Drizzle monorepo starter with auth/user vertical slice. No product domain logic; extend via the user slice pattern. --- .dockerignore | 29 + .env.example | 10 + .gitignore | 29 + .node-version | 1 + .prettierrc | 1 + .yarnrc.yml | 1 + AGENTS.md | 92 + DESIGN.md | 43 + Dockerfile | 71 + README.md | 50 + app/web/.env.example | 2 + app/web/.gitignore | 3 + app/web/AGENTS.md | 74 + app/web/astro.config.ts | 54 + app/web/components.json | 21 + app/web/package.json | 42 + app/web/src/components/AuthForm.tsx | 342 + app/web/src/components/ThemeSwitch.tsx | 53 + app/web/src/components/UserList.tsx | 183 + app/web/src/components/UserMenu.tsx | 125 + app/web/src/components/errors/Error403.tsx | 47 + app/web/src/components/errors/Error404.tsx | 49 + app/web/src/components/errors/Error500.tsx | 53 + .../src/components/errors/ErrorBoundary.tsx | 68 + app/web/src/components/errors/ErrorShell.tsx | 97 + app/web/src/components/errors/ErrorView.tsx | 48 + app/web/src/components/errors/index.ts | 11 + app/web/src/env.d.ts | 12 + app/web/src/layouts/AppLayout.astro | 217 + app/web/src/layouts/index.ts | 2 + app/web/src/lib/errors.ts | 88 + app/web/src/lib/seo.ts | 63 + app/web/src/lib/theme.ts | 45 + app/web/src/lib/trpc.ts | 21 + app/web/src/lib/utils.ts | 1 + app/web/src/middleware.ts | 84 + app/web/src/pages/403.astro | 13 + app/web/src/pages/404.astro | 13 + app/web/src/pages/500.astro | 12 + .../pages/[locale]/auth/forgot-password.astro | 19 + app/web/src/pages/[locale]/auth/login.astro | 20 + .../src/pages/[locale]/auth/register.astro | 19 + .../pages/[locale]/auth/reset-password.astro | 20 + app/web/src/pages/[locale]/dashboard.astro | 19 + app/web/src/pages/[locale]/index.astro | 20 + app/web/src/pages/[locale]/settings.astro | 19 + app/web/src/pages/api/trpc/[...trpc].ts | 51 + app/web/src/pages/api/users.ts | 22 + app/web/src/pages/llms-full.txt.ts | 63 + app/web/src/pages/llms.txt.ts | 19 + app/web/src/pages/robots.txt.ts | 16 + app/web/src/pages/sitemap.xml.ts | 47 + app/web/src/styles/globals.css | 1154 ++ app/web/src/views/auth.astro | 41 + app/web/src/views/dashboard.astro | 41 + app/web/src/views/error.astro | 35 + app/web/src/views/home.astro | 55 + app/web/tsconfig.json | 20 + biome.json | 44 + commitlint.config.mjs | 26 + .../specs/2026-09-24-i18n-design.md | 92 + lefthook.yml | 9 + package.json | 46 + packages/adsense/AGENTS.md | 42 + packages/adsense/package.json | 34 + packages/adsense/src/index.ts | 3 + packages/adsense/src/loader.ts | 70 + packages/adsense/src/react/AdSlot.tsx | 76 + packages/adsense/src/react/index.ts | 2 + packages/adsense/src/types.ts | 11 + packages/adsense/tsconfig.build.json | 10 + packages/adsense/tsconfig.json | 10 + packages/dao/AGENTS.md | 39 + packages/dao/package.json | 31 + packages/dao/src/index.ts | 7 + packages/dao/src/password-resets.ts | 38 + packages/dao/src/sessions.ts | 39 + packages/dao/src/users.ts | 65 + packages/dao/tsconfig.build.json | 14 + packages/dao/tsconfig.json | 12 + packages/db/.env.example | 3 + packages/db/.gitignore | 4 + packages/db/AGENTS.md | 38 + packages/db/drizzle.config.ts | 29 + packages/db/package.json | 35 + packages/db/src/client.ts | 17 + packages/db/src/index.ts | 7 + packages/db/tsconfig.build.json | 14 + packages/db/tsconfig.json | 11 + packages/design-tokens/AGENTS.md | 45 + packages/design-tokens/README.md | 15 + packages/design-tokens/package.json | 18 + packages/design-tokens/scripts/build.mjs | 13 + packages/design-tokens/scripts/clean.mjs | 7 + packages/design-tokens/src/tokens.css | 116 + packages/i18n/AGENTS.md | 36 + packages/i18n/package.json | 26 + packages/i18n/src/accept-language.ts | 61 + packages/i18n/src/cookie.ts | 4 + packages/i18n/src/index.ts | 15 + packages/i18n/src/locales.ts | 50 + packages/i18n/src/messages/en.ts | 99 + packages/i18n/src/messages/zh-CN.ts | 101 + packages/i18n/src/preferred.ts | 15 + packages/i18n/src/t.ts | 31 + packages/i18n/tsconfig.build.json | 13 + packages/i18n/tsconfig.json | 9 + packages/models/AGENTS.md | 38 + packages/models/package.json | 29 + packages/models/src/index.ts | 7 + packages/models/src/password-resets.ts | 23 + packages/models/src/sessions.ts | 22 + packages/models/src/users.ts | 23 + packages/models/tsconfig.build.json | 13 + packages/models/tsconfig.json | 10 + packages/seo-geo/AGENTS.md | 58 + packages/seo-geo/package.json | 37 + packages/seo-geo/src/index.ts | 68 + packages/seo-geo/src/jsonld.ts | 252 + packages/seo-geo/src/llms.ts | 190 + packages/seo-geo/src/meta.ts | 166 + packages/seo-geo/src/robots.ts | 64 + packages/seo-geo/src/sitemap.ts | 44 + packages/seo-geo/src/types.ts | 141 + packages/seo-geo/src/url.ts | 43 + packages/seo-geo/tsconfig.build.json | 13 + packages/seo-geo/tsconfig.json | 9 + packages/trpc/.gitignore | 2 + packages/trpc/AGENTS.md | 58 + packages/trpc/package.json | 38 + packages/trpc/src/context.ts | 15 + packages/trpc/src/index.ts | 29 + packages/trpc/src/load-env.ts | 15 + packages/trpc/src/pkg/error.ts | 0 packages/trpc/src/pkg/response.ts | 25 + packages/trpc/src/router.ts | 3 + packages/trpc/src/router/auth.router.ts | 78 + packages/trpc/src/router/index.ts | 43 + packages/trpc/src/router/user.router.ts | 25 + packages/trpc/src/server.ts | 45 + packages/trpc/src/services/auth.service.ts | 180 + packages/trpc/src/services/password.ts | 22 + packages/trpc/src/services/user.service.ts | 75 + packages/trpc/tsconfig.build.json | 11 + packages/trpc/tsconfig.dts.json | 18 + packages/trpc/tsconfig.json | 15 + packages/trpc/vite.config.ts | 37 + packages/types/.gitignore | 2 + packages/types/AGENTS.md | 37 + packages/types/package.json | 25 + packages/types/src/index.ts | 2 + packages/types/src/schemas.ts | 122 + packages/types/tsconfig.build.json | 10 + packages/types/tsconfig.json | 8 + packages/ui/.storybook/main.ts | 31 + packages/ui/.storybook/preview.ts | 47 + packages/ui/AGENTS.md | 54 + packages/ui/README.md | 11 + packages/ui/package.json | 46 + packages/ui/src/avatar.tsx | 44 + packages/ui/src/badge.tsx | 33 + packages/ui/src/button.tsx | 54 + packages/ui/src/card.tsx | 62 + packages/ui/src/dropdown-menu.tsx | 82 + packages/ui/src/index.ts | 33 + packages/ui/src/input.tsx | 21 + packages/ui/src/label.tsx | 18 + packages/ui/src/separator.tsx | 23 + packages/ui/src/sonner.tsx | 26 + packages/ui/src/styles/storybook.css | 15 + packages/ui/src/table.tsx | 101 + packages/ui/src/utils.ts | 7 + packages/ui/stories/avatar.stories.tsx | 28 + packages/ui/stories/badge.stories.tsx | 27 + packages/ui/stories/button.stories.tsx | 49 + packages/ui/stories/card.stories.tsx | 39 + packages/ui/stories/dropdown-menu.stories.tsx | 37 + packages/ui/stories/input.stories.tsx | 30 + packages/ui/stories/label.stories.tsx | 25 + packages/ui/stories/separator.stories.tsx | 23 + packages/ui/stories/sonner.stories.tsx | 38 + packages/ui/stories/table.stories.tsx | 46 + packages/ui/tsconfig.json | 11 + tsconfig.base.json | 20 + turbo.json | 53 + yarn.lock | 9655 +++++++++++++++++ 186 files changed, 18401 insertions(+) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .node-version create mode 100644 .prettierrc create mode 100644 .yarnrc.yml create mode 100644 AGENTS.md create mode 100644 DESIGN.md create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 app/web/.env.example create mode 100644 app/web/.gitignore create mode 100644 app/web/AGENTS.md create mode 100644 app/web/astro.config.ts create mode 100644 app/web/components.json create mode 100644 app/web/package.json create mode 100644 app/web/src/components/AuthForm.tsx create mode 100644 app/web/src/components/ThemeSwitch.tsx create mode 100644 app/web/src/components/UserList.tsx create mode 100644 app/web/src/components/UserMenu.tsx create mode 100644 app/web/src/components/errors/Error403.tsx create mode 100644 app/web/src/components/errors/Error404.tsx create mode 100644 app/web/src/components/errors/Error500.tsx create mode 100644 app/web/src/components/errors/ErrorBoundary.tsx create mode 100644 app/web/src/components/errors/ErrorShell.tsx create mode 100644 app/web/src/components/errors/ErrorView.tsx create mode 100644 app/web/src/components/errors/index.ts create mode 100644 app/web/src/env.d.ts create mode 100644 app/web/src/layouts/AppLayout.astro create mode 100644 app/web/src/layouts/index.ts create mode 100644 app/web/src/lib/errors.ts create mode 100644 app/web/src/lib/seo.ts create mode 100644 app/web/src/lib/theme.ts create mode 100644 app/web/src/lib/trpc.ts create mode 100644 app/web/src/lib/utils.ts create mode 100644 app/web/src/middleware.ts create mode 100644 app/web/src/pages/403.astro create mode 100644 app/web/src/pages/404.astro create mode 100644 app/web/src/pages/500.astro create mode 100644 app/web/src/pages/[locale]/auth/forgot-password.astro create mode 100644 app/web/src/pages/[locale]/auth/login.astro create mode 100644 app/web/src/pages/[locale]/auth/register.astro create mode 100644 app/web/src/pages/[locale]/auth/reset-password.astro create mode 100644 app/web/src/pages/[locale]/dashboard.astro create mode 100644 app/web/src/pages/[locale]/index.astro create mode 100644 app/web/src/pages/[locale]/settings.astro create mode 100644 app/web/src/pages/api/trpc/[...trpc].ts create mode 100644 app/web/src/pages/api/users.ts create mode 100644 app/web/src/pages/llms-full.txt.ts create mode 100644 app/web/src/pages/llms.txt.ts create mode 100644 app/web/src/pages/robots.txt.ts create mode 100644 app/web/src/pages/sitemap.xml.ts create mode 100644 app/web/src/styles/globals.css create mode 100644 app/web/src/views/auth.astro create mode 100644 app/web/src/views/dashboard.astro create mode 100644 app/web/src/views/error.astro create mode 100644 app/web/src/views/home.astro create mode 100644 app/web/tsconfig.json create mode 100644 biome.json create mode 100644 commitlint.config.mjs create mode 100644 docs/superpowers/specs/2026-09-24-i18n-design.md create mode 100644 lefthook.yml create mode 100644 package.json create mode 100644 packages/adsense/AGENTS.md create mode 100644 packages/adsense/package.json create mode 100644 packages/adsense/src/index.ts create mode 100644 packages/adsense/src/loader.ts create mode 100644 packages/adsense/src/react/AdSlot.tsx create mode 100644 packages/adsense/src/react/index.ts create mode 100644 packages/adsense/src/types.ts create mode 100644 packages/adsense/tsconfig.build.json create mode 100644 packages/adsense/tsconfig.json create mode 100644 packages/dao/AGENTS.md create mode 100644 packages/dao/package.json create mode 100644 packages/dao/src/index.ts create mode 100644 packages/dao/src/password-resets.ts create mode 100644 packages/dao/src/sessions.ts create mode 100644 packages/dao/src/users.ts create mode 100644 packages/dao/tsconfig.build.json create mode 100644 packages/dao/tsconfig.json create mode 100644 packages/db/.env.example create mode 100644 packages/db/.gitignore create mode 100644 packages/db/AGENTS.md create mode 100644 packages/db/drizzle.config.ts create mode 100644 packages/db/package.json create mode 100644 packages/db/src/client.ts create mode 100644 packages/db/src/index.ts create mode 100644 packages/db/tsconfig.build.json create mode 100644 packages/db/tsconfig.json create mode 100644 packages/design-tokens/AGENTS.md create mode 100644 packages/design-tokens/README.md create mode 100644 packages/design-tokens/package.json create mode 100644 packages/design-tokens/scripts/build.mjs create mode 100644 packages/design-tokens/scripts/clean.mjs create mode 100644 packages/design-tokens/src/tokens.css create mode 100644 packages/i18n/AGENTS.md create mode 100644 packages/i18n/package.json create mode 100644 packages/i18n/src/accept-language.ts create mode 100644 packages/i18n/src/cookie.ts create mode 100644 packages/i18n/src/index.ts create mode 100644 packages/i18n/src/locales.ts create mode 100644 packages/i18n/src/messages/en.ts create mode 100644 packages/i18n/src/messages/zh-CN.ts create mode 100644 packages/i18n/src/preferred.ts create mode 100644 packages/i18n/src/t.ts create mode 100644 packages/i18n/tsconfig.build.json create mode 100644 packages/i18n/tsconfig.json create mode 100644 packages/models/AGENTS.md create mode 100644 packages/models/package.json create mode 100644 packages/models/src/index.ts create mode 100644 packages/models/src/password-resets.ts create mode 100644 packages/models/src/sessions.ts create mode 100644 packages/models/src/users.ts create mode 100644 packages/models/tsconfig.build.json create mode 100644 packages/models/tsconfig.json create mode 100644 packages/seo-geo/AGENTS.md create mode 100644 packages/seo-geo/package.json create mode 100644 packages/seo-geo/src/index.ts create mode 100644 packages/seo-geo/src/jsonld.ts create mode 100644 packages/seo-geo/src/llms.ts create mode 100644 packages/seo-geo/src/meta.ts create mode 100644 packages/seo-geo/src/robots.ts create mode 100644 packages/seo-geo/src/sitemap.ts create mode 100644 packages/seo-geo/src/types.ts create mode 100644 packages/seo-geo/src/url.ts create mode 100644 packages/seo-geo/tsconfig.build.json create mode 100644 packages/seo-geo/tsconfig.json create mode 100644 packages/trpc/.gitignore create mode 100644 packages/trpc/AGENTS.md create mode 100644 packages/trpc/package.json create mode 100644 packages/trpc/src/context.ts create mode 100644 packages/trpc/src/index.ts create mode 100644 packages/trpc/src/load-env.ts create mode 100644 packages/trpc/src/pkg/error.ts create mode 100644 packages/trpc/src/pkg/response.ts create mode 100644 packages/trpc/src/router.ts create mode 100644 packages/trpc/src/router/auth.router.ts create mode 100644 packages/trpc/src/router/index.ts create mode 100644 packages/trpc/src/router/user.router.ts create mode 100644 packages/trpc/src/server.ts create mode 100644 packages/trpc/src/services/auth.service.ts create mode 100644 packages/trpc/src/services/password.ts create mode 100644 packages/trpc/src/services/user.service.ts create mode 100644 packages/trpc/tsconfig.build.json create mode 100644 packages/trpc/tsconfig.dts.json create mode 100644 packages/trpc/tsconfig.json create mode 100644 packages/trpc/vite.config.ts create mode 100644 packages/types/.gitignore create mode 100644 packages/types/AGENTS.md create mode 100644 packages/types/package.json create mode 100644 packages/types/src/index.ts create mode 100644 packages/types/src/schemas.ts create mode 100644 packages/types/tsconfig.build.json create mode 100644 packages/types/tsconfig.json create mode 100644 packages/ui/.storybook/main.ts create mode 100644 packages/ui/.storybook/preview.ts create mode 100644 packages/ui/AGENTS.md create mode 100644 packages/ui/README.md create mode 100644 packages/ui/package.json create mode 100644 packages/ui/src/avatar.tsx create mode 100644 packages/ui/src/badge.tsx create mode 100644 packages/ui/src/button.tsx create mode 100644 packages/ui/src/card.tsx create mode 100644 packages/ui/src/dropdown-menu.tsx create mode 100644 packages/ui/src/index.ts create mode 100644 packages/ui/src/input.tsx create mode 100644 packages/ui/src/label.tsx create mode 100644 packages/ui/src/separator.tsx create mode 100644 packages/ui/src/sonner.tsx create mode 100644 packages/ui/src/styles/storybook.css create mode 100644 packages/ui/src/table.tsx create mode 100644 packages/ui/src/utils.ts create mode 100644 packages/ui/stories/avatar.stories.tsx create mode 100644 packages/ui/stories/badge.stories.tsx create mode 100644 packages/ui/stories/button.stories.tsx create mode 100644 packages/ui/stories/card.stories.tsx create mode 100644 packages/ui/stories/dropdown-menu.stories.tsx create mode 100644 packages/ui/stories/input.stories.tsx create mode 100644 packages/ui/stories/label.stories.tsx create mode 100644 packages/ui/stories/separator.stories.tsx create mode 100644 packages/ui/stories/sonner.stories.tsx create mode 100644 packages/ui/stories/table.stories.tsx create mode 100644 packages/ui/tsconfig.json create mode 100644 tsconfig.base.json create mode 100644 turbo.json create mode 100644 yarn.lock diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..82fc346 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,29 @@ +# 依赖与缓存 +node_modules +**/node_modules +.yarn/install-state.gz +.turbo +**/.turbo + +# 构建产物(镜像内重新 build) +**/dist +**/.astro +output + +# VCS / 本地环境 +.git +.env +.env.* +!.env.example +*.db +local.db +*.log +*.tsbuildinfo +**/*.tsbuildinfo +.DS_Store + +# 本地文档与元数据(不影响构建) +AGENTS.md +DESIGN.md +README.md +.prettierrc diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..157594e --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +# Turso 数据库连接 +# 本地开发用 sqlite,线上用 turso +TURSO_DATABASE_URL=file:local.db + +# 生产环境(替换为实际 turso URL) +# TURSO_DATABASE_URL=libsql://.turso.io +# TURSO_AUTH_TOKEN=your_token_here + +# SEO 规范源(canonical / sitemap / og:url 使用的真实域名,生产必填) +# PUBLIC_SITE_URL=https://tool.example.com diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..523b8a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +node_modules/ +dist/ +.astro/ +*.local +.env +.env.* +!.env.example +.playwright-cli/ + +# build / tooling caches +.turbo/ +.tsbuildinfo* +*.tsbuildinfo +.yarn/install-state.gz +*.log +**/.turbo/ +output/ + +# local database +local.db +*.db +!*.db.sql + +# build outputs already covered by dist/, but ensure package-level +**/dist/ +storybook-static/ +**/storybook-static/ + +.DS_Store \ No newline at end of file diff --git a/.node-version b/.node-version new file mode 100644 index 0000000..91d2624 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +26.7.0 diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..cdb2b85 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{"semi":false,"singleQuote":true,"trailingComma":"all","printWidth":100} \ No newline at end of file diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..3186f3f --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f14aa1a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,92 @@ +# AGENTS.md — app-template 总览 + +面向 AI 编码代理的仓库导览。修改任何代码前,先读本文件与目标子包的 `AGENTS.md`。 + +## 项目是什么 + +**可复用全栈脚手架 / 开发模板**(不绑定产品业务)。只保留鉴权 + `user` 垂直示例,演示完整分层接线。视觉基线见 `DESIGN.md`。 + +技术栈:Astro 7(SSR)+ React 19 islands · tRPC v11 · Drizzle ORM + Turso/libSQL · Tailwind CSS 4 · TypeScript 7 · Biome · Yarn 4 workspaces + Turbo。 + +## 模块地图 + +| 包 | 职责 | 依赖 | +|----|------|------| +| `@app/types` | Zod 入参 schema + 共享输出类型 | zod | +| `@app/models` | Drizzle 表定义(唯一 schema 源) | drizzle-orm | +| `@app/db` | libSQL/Turso 客户端单例 + drizzle-kit | models | +| `@app/dao` | 数据访问层(`userDao` 等) | db, models | +| `@app/trpc` | tRPC 路由 + service 业务逻辑(调用 DAO) | types, dao | +| `@app/web` | Astro 前端 + React islands + 本地 tRPC HTTP 适配 | trpc, types, design-tokens, seo-geo, i18n, ui | +| `@app/ui` | shadcn 风格基础 UI 组件(源码直出 + Storybook) | design-tokens, react (peer) | +| `@app/design-tokens` | 共享 CSS 设计令牌(`tokens.css`) | — | +| `@app/seo-geo` | SEO/GEO 纯函数(meta、sitemap、JSON-D、llms.txt) | zod, schema-dts, escape-html, fast-xml-builder, serialize-javascript, mdast-util-to-markdown | +| `@app/i18n` | 文案与 locale 纯函数(zh-CN / en、t()、Accept-Language) | — | +| `@app/adsense` | AdSense 脚本加载 + `AdSlot` React 组件 | react (peer) | + +依赖方向(只允许向下): + +``` +web ──► trpc ──► dao ──► db ──► models + │ │ │ + │ └─────────────────┴──► types + ├──► design-tokens + ├──► seo-geo + ├──► i18n + ├──► adsense + └──► ui +``` + +禁止循环依赖。跨包只通过 `package.json` 的 `exports` 入口引用,不要 deep import。 + +## 目录约定 + +- 源码在各包 `src/`;构建产物在 `dist/`(git 忽略,勿手改)。 +- 环境变量模板见 `.env.example`:`TURSO_DATABASE_URL`、`TURSO_AUTH_TOKEN`、`PUBLIC_SITE_URL`。 +- 根 `.env` 由 `packages/db/drizzle.config.ts` 手动加载(drizzle-kit 在 monorepo 中不会自动读根 env)。 +- **模板不掺杂业务逻辑**:禁止把产品领域实体/页面写进本仓库;下游业务仓库再扩展。 + +## 常用命令 + +```bash +yarn install +yarn typecheck # 全仓 typecheck +yarn lint # Biome +yarn build # 全仓 build(依赖拓扑) +yarn build:web # 仅 @app/web + 上游依赖闭包(日常/CI 更快) +yarn dev:web # Astro :4321 +yarn dev:api # 独立 tRPC :4000(web 内嵌 /api/trpc 时通常不需要) +yarn db:generate # Drizzle 迁移生成 +yarn db:push # 推 schema +yarn db:studio +``` + +改某个包时可先 `yarn workspace @app/ typecheck`。 + +## 全局约定(必须遵守) + +1. **TypeScript 很严**:`strict` + `noUncheckedIndexedAccess` + `exactOptionalPropertyTypes` + `noPropertyAccessFromIndexSignature`。可选属性传 `undefined` 时类型要写 `?: T | undefined`。 +2. **ESM only**(`"type": "module"`)。源码内相对 import 带 `.js` 后缀(TS bundler 解析)。 +3. **格式化/静态检查**:Biome(2 空格、行宽 100)。不要引入 ESLint/Prettier。 +4. **校验**:对外输入用 Zod 4,定义放在 `@app/types`,router/service 共用。 +5. **数据库模型**只放 `@app/models`;**查询/SQL** 只放 `@app/dao`;`@app/db` 只建连接单例,不建表、不含业务规则。 +6. **UI**:设计令牌从 `@app/design-tokens` 引入;组件优先用 `@app/ui`(shadcn 风格,源码在 `packages/ui`)+ `cn()`。视觉遵循 `DESIGN.md`。 +7. **构建顺序**:改 `types`/`models` 后需先 build 上游,再让下游 typecheck(Turbo `dependsOn: ["^build"]` 已配置)。 +8. **注释**:只写非显而易见的 WHY;默认不写注释。 +9. **新增业务实体**:复制 `user` 垂直切片(types → models → dao → trpc → web),不要跳层。 + +## 各包入口 + +细节见各包 `AGENTS.md`: + +- [packages/types/AGENTS.md](packages/types/AGENTS.md) +- [packages/models/AGENTS.md](packages/models/AGENTS.md) +- [packages/db/AGENTS.md](packages/db/AGENTS.md) +- [packages/dao/AGENTS.md](packages/dao/AGENTS.md) +- [packages/trpc/AGENTS.md](packages/trpc/AGENTS.md) +- [app/web/AGENTS.md](app/web/AGENTS.md) +- [packages/design-tokens/AGENTS.md](packages/design-tokens/AGENTS.md) +- [packages/seo-geo/AGENTS.md](packages/seo-geo/AGENTS.md) +- [packages/i18n/AGENTS.md](packages/i18n/AGENTS.md) +- [packages/adsense/AGENTS.md](packages/adsense/AGENTS.md) +- [packages/ui/AGENTS.md](packages/ui/AGENTS.md) diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..7d8d7f7 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,43 @@ +# App Template — 设计方向 + +脚手架视觉基线:**深空工业中控终端**。不绑定具体产品叙事;下游可在此 palette / type 上换肤,但 **`@app/design-tokens` 色板 token 路径约定不变**(改值可以,改结构需同步全局)。 + +## Style anchor +深色工业 HUD / 控制台:近黑底、信号黄强调、发丝线框、等宽铭牌。 + +## Palette +| 角色 | 值 | +|------|-----| +| 背景(近黑) | `#0E0F0E` | +| 表面 | `#191919` | +| 下沉 | `#121212` | +| 线框 | `#2E2E2E` | +| 线框(强) | `#35373C` | +| 墨色 | `#F2F2F2` | +| 弱化文字 | `#999999` | +| 更弱文字 | `#666666` | +| 强调(信号黄) | `#FFFA00` | +| 信号绿 | `#00FFA2` | +| 信号品红 | `#FF1AAC` | +| 亮色背景(对照) | `#FAFAFA` | + +## Typography +- Display:`Space Grotesk` + `Noto Sans SC` 900 +- 正文 CJK:`Noto Sans SC` / `PingFang SC` +- 等宽:`JetBrains Mono`(编号、快捷键、路径、状态读出) +- 字阶:10–11px 铭牌 / 13–14px 正文 / 28–48px 标题;标签全大写 + `0.16–0.22em` 字距 + +## Layout +- 左侧固定竖栏(品牌 + 导航 + 主题) +- 内容壳 + 顶栏;分隔用点线 `u-dotline` / 虚线 +- 按钮 `u-btn`;卡片 `u-card`;间距 4px 节奏;小圆角 3–12px +- **克制装饰**:禁贴纸堆叠;色彩以 tokens 为准 + +## Interaction +- 主题:系统 / 亮 / 暗(`app-theme`) +- 语言:cookie `app-lang` + Accept-Language +- SSR 输出结构与内容;动效只在浏览器 effect 内绑定 +- 尊重 `prefers-reduced-motion` + +## 范围 +`tokens.css` · `globals.css` · `AppLayout.astro` · `ThemeSwitch` · `UserMenu` · `home` / `dashboard` 视图 · `ui/*` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..96f01a1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,71 @@ +# syntax=docker/dockerfile:1 +# 与 .node-version 对齐;可用 build-arg 覆盖 +ARG NODE_VERSION=26.7.0 + +FROM node:${NODE_VERSION}-alpine AS base +WORKDIR /app +ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ + TURBO_TELEMETRY_DISABLED=1 \ + DO_NOT_TRACK=1 +# Node 25+ 官方镜像不再内置 corepack,需显式安装以使用 yarn@4 +RUN npm install -g corepack && corepack enable + +# ---- 依赖层:只拷清单,最大化 yarn 缓存 ---- +FROM base AS deps +COPY package.json yarn.lock .yarnrc.yml ./ +COPY app/web/package.json app/web/package.json +# workspaces: packages/* — 清单缺一不可,否则 yarn 解析 workspace:* 失败或漏链 +COPY packages/adsense/package.json packages/adsense/package.json +COPY packages/dao/package.json packages/dao/package.json +COPY packages/db/package.json packages/db/package.json +COPY packages/design-tokens/package.json packages/design-tokens/package.json +COPY packages/i18n/package.json packages/i18n/package.json +COPY packages/models/package.json packages/models/package.json +COPY packages/seo-geo/package.json packages/seo-geo/package.json +COPY packages/trpc/package.json packages/trpc/package.json +COPY packages/types/package.json packages/types/package.json +COPY packages/ui/package.json packages/ui/package.json +RUN --mount=type=cache,target=/root/.yarn/berry/cache \ + yarn install --immutable + +# ---- 构建层:只 build web 依赖闭包 + 裁剪生产依赖 ---- +FROM deps AS build +COPY . . +# Astro 构建期内联 PUBLIC_*;空串会导致 canonical/sitemap 抛 Invalid URL, +# 因此给 localhost 默认值,生产用 --build-arg PUBLIC_SITE_URL=https://example.com 覆盖 +ARG PUBLIC_SITE_URL=http://localhost:4321 +ENV PUBLIC_SITE_URL=${PUBLIC_SITE_URL} \ + ASTRO_TELEMETRY_DISABLED=1 \ + TURBO_TELEMETRY_DISABLED=1 +# --filter=@app/web...:web + 其上游依赖;跳过 adsense 等无关包 +# --cache-dir 指到 /tmp:与 .turbo 分离,rm -rf .turbo 不会清掉 BuildKit 缓存挂载 +# 缓存挂载需 BuildKit(DOCKER_BUILDKIT=1) +RUN --mount=type=cache,id=turbo-cache,target=/tmp/turbo-cache \ + yarn turbo run build --filter=@app/web... --cache-dir=/tmp/turbo-cache \ + && yarn workspaces focus --production @app/web \ + && rm -rf .git .turbo output \ + && rm -rf packages/*/src packages/*/*.tsbuildinfo \ + && rm -rf app/web/src app/web/node_modules/.vite + +# ---- 运行层:仅产物 + 生产 node_modules ---- +FROM node:${NODE_VERSION}-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production \ + HOST=0.0.0.0 \ + HOSTNAME=0.0.0.0 \ + PORT=4321 \ + # file: 库与 session 的可写位置(/app 属主为 root,node 用户不可写) + TMPDIR=/tmp + +COPY --from=build --chown=node:node /app/package.json /app/yarn.lock /app/.yarnrc.yml ./ +COPY --from=build --chown=node:node /app/app/web/package.json app/web/package.json +COPY --from=build --chown=node:node /app/app/web/dist app/web/dist +COPY --from=build --chown=node:node /app/packages packages +COPY --from=build --chown=node:node /app/node_modules node_modules +# 工作区内可能存在的局部 node_modules(会话缓存写在其 .astro 下) +COPY --from=build --chown=node:node /app/app/web/node_modules app/web/node_modules +RUN mkdir -p /app/data && chown node:node /app/data + +USER node +EXPOSE 4321 +CMD ["node", "app/web/dist/server/entry.mjs"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..588f928 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# App Template — Astro + tRPC + Drizzle Monorepo + +可复用全栈脚手架:**不绑定产品业务**,只保留鉴权 + `user` 垂直示例,演示完整分层接线。 + +``` +. +├── app/ +│ └── web/ @app/web — Astro SSR + React islands + i18n 路由 +├── packages/ +│ ├── types/ @app/types — Zod 入参 schema + 共享 DTO +│ ├── models/ @app/models — Drizzle 表定义(唯一 schema 源) +│ ├── db/ @app/db — libSQL/Turso 连接单例 + drizzle-kit +│ ├── dao/ @app/dao — 数据访问层 +│ ├── trpc/ @app/trpc — tRPC 路由 + service +│ ├── ui/ @app/ui — shadcn 风格基础组件 + Storybook +│ ├── design-tokens/ @app/design-tokens +│ ├── seo-geo/ @app/seo-geo — SEO/GEO 纯函数 +│ ├── i18n/ @app/i18n — zh-CN / en 文案与 locale 工具 +│ └── adsense/ @app/adsense — AdSense 加载(可选) +└── docs/ +``` + +## 快速开始 + +```bash +yarn install + +cp .env.example .env +# 填入 TURSO_DATABASE_URL / TURSO_AUTH_TOKEN / PUBLIC_SITE_URL + +yarn db:push # 推 schema +yarn dev:web # Astro :4321 +``` + +## 技术栈 + +| 层 | 技术 | +|------|------| +| 前端 | Astro 7 + React 19 islands | +| API | tRPC v11 | +| ORM | Drizzle + @libsql/client(Turso) | +| 样式 | Tailwind CSS 4 + design-tokens | +| 校验 | Zod 4 | +| 工程 | TypeScript 7 · Biome · Yarn 4 · Turbo | + +## 分层(必须遵守) + +`web → trpc → dao → db → models`,横切 `types`。禁止循环依赖;跨包只走 `package.json` 的 `exports`。 + +新增业务实体请复制 `user` 垂直切片:`types` schema → `models` 表 → `dao` 查询 → `trpc` service/router → `web` 页面。细节见根目录 `AGENTS.md`。 diff --git a/app/web/.env.example b/app/web/.env.example new file mode 100644 index 0000000..58b9e64 --- /dev/null +++ b/app/web/.env.example @@ -0,0 +1,2 @@ +# Point to the tRPC API server +PUBLIC_API_URL=http://localhost:4000 diff --git a/app/web/.gitignore b/app/web/.gitignore new file mode 100644 index 0000000..262e83b --- /dev/null +++ b/app/web/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +.astro/ \ No newline at end of file diff --git a/app/web/AGENTS.md b/app/web/AGENTS.md new file mode 100644 index 0000000..5a096c4 --- /dev/null +++ b/app/web/AGENTS.md @@ -0,0 +1,74 @@ +# AGENTS.md — @app/web + +## 职责 + +Astro SSR 前端(Node standalone):页面、布局、React islands、tRPC HTTP 适配、SEO 文本端点、**i18n 路由**。 + +脚手架基线页面:首页、鉴权、仪表盘(user 列表示例)、设置。视觉遵循仓库根 `DESIGN.md` 与 `@app/design-tokens`。 + +## i18n + +- 语言:`zh-CN`(`/zh-CN/...`)、`en`(`/en/...`)。**中文也带前缀**,不把中文留在裸 `/`。 +- **单套路由**:`src/pages/[locale]/index.astro`、`[locale]/dashboard.astro` + `src/views/*`;禁止再复制 `pages/zh-CN`、`pages/en` 目录。 +- 文案:`@app/i18n` 的 `t(locale, key)`;React island 传 `locale` prop,不引入 i18next。 +- 裸 `/`:`src/middleware.ts` 按 cookie `app-lang` → `Accept-Language` 跳 `/zh-CN` 或 `/en`;响应 `Vary: Accept-Language`;非法/大小写 `zh-cn` 规范化。 +- 手动切换:header 语言链接写 cookie 后跳转(布局内联脚本)。 +- 设计说明:`docs/superpowers/specs/2026-09-24-i18n-design.md`。 + +## 结构 + +``` +src/ + pages/ + [locale]/index.astro | dashboard.astro | auth/* | settings.astro + api/trpc/[...trpc].ts | api/users.ts + sitemap.xml.ts | robots.txt.ts | llms.txt.ts | llms-full.txt.ts + views/ + home.astro | dashboard.astro | auth.astro | error.astro + middleware.ts # 裸 / 语言检测跳转 + locale 规范化 + layouts/AppLayout.astro + components/ + UserList.tsx | UserMenu.tsx | ThemeSwitch.tsx | AuthForm.tsx | errors/* + lib/ + trpc.ts | seo.ts(getSiteSeo(locale))| theme.ts | utils.ts(cn re-export) + styles/globals.css +astro.config.ts +``` + +路径别名 + +- `~/*`、`@/*` → `src/*` +- `@app/i18n` / `@app/trpc` / `@app/types` / `@app/db` / `@app/seo-geo` → 各包 `dist`(astro.config.ts) +- `@app/ui` → `packages/ui/src`(源码直出) +- typecheck 时 tsconfig paths 指向各包 `src`——**改上游后先 build 再 dev/build web** + +## 页面与 SEO + +- 布局统一走 `AppLayout.astro`,必传 `locale`,通过 `seo` prop 传 `PageSeoInput`。 +- 站点 SEO:`lib/seo.ts` 的 `getSiteSeo(locale)`;sitemap 双语 URL + hreflang `alternates`。 +- 新公开页面:登记 `PUBLIC_PATHS` / views,并同步 `getSiteSeo` 的 `llms.pages` 与 sitemap。 + +## React islands + +- 需要交互才加 `client:load` / `client:visible`。 +- 文案:`import { t } from "@app/i18n"` + `locale` prop;不要绑 i18next Provider。 +- tRPC:组件内 `QueryClientProvider` + trpc Provider(参考 `UserList.tsx`)。 +- UI:`@app/ui`(通用基础组件)+ `cn()`(`~/lib/utils` re-export);颜色用 CSS 变量。 + +## 修改指南 + +- **加文案**:先改 `packages/i18n/src/messages/zh-CN.ts`,再补 `en.ts`。 +- **加业务页面**:在下游仓库扩展,勿把产品业务写回模板。 +- **调 API**:优先 tRPC;`api/users.ts` 仅演示。 +- **主题**:`localStorage` key `app-theme`;语言 cookie `app-lang`。 +- Astro 保留字:组件 props 避免 `slot`。 + +## 命令 + +```bash +yarn workspace @app/web typecheck +yarn workspace @app/web lint +yarn workspace @app/web dev # :4321 +yarn workspace @app/web build +yarn dev:web +``` diff --git a/app/web/astro.config.ts b/app/web/astro.config.ts new file mode 100644 index 0000000..b8da8a6 --- /dev/null +++ b/app/web/astro.config.ts @@ -0,0 +1,54 @@ +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import node from "@astrojs/node"; +import react from "@astrojs/react"; +import tailwindcss from "@tailwindcss/vite"; +import { defineConfig } from "astro/config"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +// monorepo 根 .env(Astro 默认只读 app/web/.env,SSR 里 @app/db 要 process.env.TURSO_*) +try { + const env = readFileSync(resolve(__dirname, "../../.env"), "utf-8"); + for (const line of env.split("\n")) { + const [key, ...vals] = line.split("="); + if (key && !key.startsWith("#")) { + process.env[key.trim()] ??= vals.join("=").trim(); + } + } +} catch {} + +// SSR + Node standalone;@app/* 别名指向各包 dist(dev/build 前需先 build 上游) +export default defineConfig({ + output: "server", + adapter: node({ mode: "standalone" }), + integrations: [react()], + // 路由:动态 [locale]/… 单套视图;/ 与非法前缀在 middleware 处理 + vite: { + plugins: [tailwindcss()], + optimizeDeps: { + // 预打包 react CJS,避免浏览器拿裸 CJS 导致 island 无 createRoot + include: ["react", "react-dom", "react-dom/client"], + }, + resolve: { + alias: { + "@/*": resolve(__dirname, "src"), + "@app/i18n": resolve(__dirname, "../../packages/i18n/dist/index.js"), + "@app/trpc": resolve(__dirname, "../../packages/trpc/dist/index.js"), + "@app/types": resolve(__dirname, "../../packages/types/dist/index.js"), + "@app/db": resolve(__dirname, "../../packages/db/dist/index.js"), + "@app/seo-geo": resolve(__dirname, "../../packages/seo-geo/dist/index.js"), + "@app/ui": resolve(__dirname, "../../packages/ui/src/index.ts"), + }, + dedupe: ["react", "react-dom"], + }, + ssr: { + noExternal: ["@app/trpc", "@app/db", "@app/i18n", "@app/ui"], + }, + }, + server: { + port: 4321, + host: false, + }, +}); diff --git a/app/web/components.json b/app/web/components.json new file mode 100644 index 0000000..8b89406 --- /dev/null +++ b/app/web/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "~/components", + "utils": "~/lib/utils", + "ui": "../../packages/ui/src", + "lib": "~/lib", + "hooks": "~/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/app/web/package.json b/app/web/package.json new file mode 100644 index 0000000..9594974 --- /dev/null +++ b/app/web/package.json @@ -0,0 +1,42 @@ +{ + "name": "@app/web", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro", + "typecheck": "tsc --noEmit", + "lint": "biome check ." + }, + "dependencies": { + "@app/design-tokens": "workspace:*", + "@app/i18n": "workspace:*", + "@app/seo-geo": "workspace:*", + "@app/trpc": "workspace:*", + "@app/types": "workspace:*", + "@app/ui": "workspace:*", + "@astrojs/node": "^11.1.6", + "@astrojs/react": "^4.3.0", + "@tailwindcss/vite": "^4.3.3", + "@tanstack/react-query": "^5.75.0", + "@trpc/client": "^11.19.0", + "@trpc/react-query": "^11.19.0", + "astro": "^7.3.3", + "gsap": "^3.15.0", + "lenis": "^1.3.26", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "tailwindcss": "^4.3.3" + }, + "devDependencies": { + "@astrojs/check": "^0.9.10", + "@biomejs/biome": "^2.1.1", + "@trpc/server": "^11.19.0", + "@types/react": "^19.1.4", + "@types/react-dom": "^19.1.5", + "typescript": "^7.0.2" + } +} diff --git a/app/web/src/components/AuthForm.tsx b/app/web/src/components/AuthForm.tsx new file mode 100644 index 0000000..1ab4456 --- /dev/null +++ b/app/web/src/components/AuthForm.tsx @@ -0,0 +1,342 @@ +/** + * 认证表单岛:登录 / 注册 / 找回 / 重置 / 改密,按 `mode` 切换。 + * Provider 包在组件内部(与 UserList 同模式)。 + */ +import { useState } from "react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { trpc, createTRPCReactClient } from "../lib/trpc"; +import type { Locale } from "@app/i18n"; +import { t } from "@app/i18n"; +import { + Button, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, + Input, + Label, + toast, +} from "@app/ui"; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { retry: false, refetchOnWindowFocus: false }, + }, +}); +const trpcClient = createTRPCReactClient(); + +export type AuthFormMode = "login" | "register" | "forgot" | "reset" | "change"; + +export interface AuthFormProps { + locale: Locale; + mode: AuthFormMode; + /** 登录成功后的跳转地址 */ + next?: string | undefined; + /** 重置页预填令牌 */ + defaultToken?: string | undefined; +} + +export default function AuthForm(props: AuthFormProps) { + return ( + + + + + + ); +} + +function AuthFormInner({ locale, mode, next, defaultToken }: AuthFormProps) { + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [currentPassword, setCurrentPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [token, setToken] = useState(defaultToken ?? ""); + const [resetToken, setResetToken] = useState(null); + + const login = trpc.auth.login.useMutation({ + onSuccess: () => { + toast.success(t(locale, "auth.msg.loginOk")); + window.location.href = next?.startsWith("/") ? next : home(locale); + }, + onError: (err) => toast.error(err.message || t(locale, "auth.err.generic")), + }); + + const register = trpc.auth.register.useMutation({ + onSuccess: () => { + toast.success(t(locale, "auth.msg.registerOk")); + window.location.href = next?.startsWith("/") ? next : home(locale); + }, + onError: (err) => toast.error(err.message || t(locale, "auth.err.generic")), + }); + + const requestReset = trpc.auth.requestPasswordReset.useMutation({ + onSuccess: (data) => { + toast.success(t(locale, "auth.msg.resetRequested")); + if (data.resetToken) { + setResetToken(data.resetToken); + setToken(data.resetToken); + } + }, + onError: (err) => toast.error(err.message || t(locale, "auth.err.generic")), + }); + + const resetPassword = trpc.auth.resetPassword.useMutation({ + onSuccess: () => { + toast.success(t(locale, "auth.msg.resetOk")); + window.location.href = `${home(locale)}/auth/login`; + }, + onError: (err) => toast.error(err.message || t(locale, "auth.err.generic")), + }); + + const changePassword = trpc.auth.changePassword.useMutation({ + onSuccess: () => { + toast.success(t(locale, "auth.msg.passwordOk")); + setCurrentPassword(""); + setNewPassword(""); + }, + onError: (err) => toast.error(err.message || t(locale, "auth.err.generic")), + }); + + const busy = + login.isPending || + register.isPending || + requestReset.isPending || + resetPassword.isPending || + changePassword.isPending; + + function onSubmit(e: React.FormEvent) { + e.preventDefault(); + if (busy) return; + if (mode === "login") { + login.mutate({ email, password }); + } else if (mode === "register") { + register.mutate({ name, email, password }); + } else if (mode === "forgot") { + requestReset.mutate({ email }); + } else if (mode === "reset") { + resetPassword.mutate({ token, newPassword }); + } else if (mode === "change") { + changePassword.mutate({ currentPassword, newPassword }); + } + } + + const titles: Record = { + login: { + title: t(locale, "auth.login.heading"), + desc: t(locale, "auth.login.subtitle"), + }, + register: { + title: t(locale, "auth.register.heading"), + desc: t(locale, "auth.register.subtitle"), + }, + forgot: { + title: t(locale, "auth.forgot.heading"), + desc: t(locale, "auth.forgot.subtitle"), + }, + reset: { + title: t(locale, "auth.reset.heading"), + desc: t(locale, "auth.reset.subtitle"), + }, + change: { + title: t(locale, "auth.settings.changeHeading"), + desc: t(locale, "auth.settings.hint"), + }, + }; + + const homeBase = home(locale); + + return ( + + + {titles[mode].title} + {titles[mode].desc} + + +
+ {mode === "register" && ( +
+ + setName(e.target.value)} + /> +
+ )} + + {(mode === "login" || mode === "register" || mode === "forgot") && ( +
+ + setEmail(e.target.value)} + /> +
+ )} + + {mode === "login" && ( +
+ + setPassword(e.target.value)} + /> +
+ )} + + {mode === "register" && ( +
+ + setPassword(e.target.value)} + /> +
+ )} + + {mode === "change" && ( + <> +
+ + setCurrentPassword(e.target.value)} + /> +
+
+ + setNewPassword(e.target.value)} + /> +
+ + )} + + {mode === "reset" && ( + <> +
+ + setToken(e.target.value)} + /> +
+
+ + setNewPassword(e.target.value)} + /> +
+ + )} + + +
+ + {mode === "forgot" && resetToken && ( +
+

+ {t(locale, "auth.forgot.tokenHint")} +

+ {resetToken} + + {t(locale, "auth.forgot.toReset")} + +
+ )} + +
+ {mode === "login" && ( + <> + + {t(locale, "auth.forgot.title")} + + + {t(locale, "auth.login.noAccount")}{" "} + + {t(locale, "auth.login.toRegister")} + + + + )} + {mode === "register" && ( + + {t(locale, "auth.register.hasAccount")}{" "} + + {t(locale, "auth.register.toLogin")} + + + )} + {(mode === "forgot" || mode === "reset") && ( + + {t(locale, "auth.forgot.toLogin")} + + )} +
+
+
+ ); +} + +function home(locale: Locale): string { + return locale === "zh-CN" ? "/zh-CN" : "/en"; +} diff --git a/app/web/src/components/ThemeSwitch.tsx b/app/web/src/components/ThemeSwitch.tsx new file mode 100644 index 0000000..0116c4a --- /dev/null +++ b/app/web/src/components/ThemeSwitch.tsx @@ -0,0 +1,53 @@ +import { useEffect, useState } from "react"; +import { + applyTheme, + readThemeMode, + watchSystemTheme, + writeThemeMode, + type ThemeMode, +} from "~/lib/theme"; + +const ORDER: ThemeMode[] = ["system", "light", "dark"]; +const LABEL: Record = { + system: "SYS", + light: "LIGHT", + dark: "DARK", +}; +const ICON: Record = { + system: "◎", + light: "☀", + dark: "☾", +}; + +export default function ThemeSwitch() { + const [mode, setMode] = useState("system"); + + useEffect(() => { + const current = readThemeMode(); + setMode(current); + applyTheme(current); + return watchSystemTheme(() => { + if (readThemeMode() === "system") applyTheme("system"); + }); + }, []); + + const cycle = () => { + const next = ORDER[(ORDER.indexOf(mode) + 1) % ORDER.length] ?? "system"; + setMode(next); + writeThemeMode(next); + applyTheme(next); + }; + + return ( + + ); +} diff --git a/app/web/src/components/UserList.tsx b/app/web/src/components/UserList.tsx new file mode 100644 index 0000000..39a4766 --- /dev/null +++ b/app/web/src/components/UserList.tsx @@ -0,0 +1,183 @@ +/** + * 用户 CRUD 演示岛:tRPC + React Query。 + * Provider 包在组件内部,便于 Astro 以 client:visible 挂载而不污染全局。 + */ + +import type { RouterOutputs } from "@app/trpc"; +import { + Badge, + Button, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, + Input, + Label, + Separator, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + toast, +} from "@app/ui"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { useState } from "react"; +import { createTRPCReactClient, trpc } from "../lib/trpc"; + +const queryClient = new QueryClient(); +const trpcClient = createTRPCReactClient(); + +type User = RouterOutputs["user"]["list"]["users"][number]; + +export default function UserList() { + return ( + + + + + + ); +} + +function UserListInner() { + const users = trpc.user.list.useQuery({ limit: 20, offset: 0 }); + const createUser = trpc.user.create.useMutation({ + onSuccess: () => { + users.refetch(); + toast.success("用户创建成功"); + }, + onError: (err) => { + toast.error(`创建失败: ${err.message}`); + }, + }); + + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!name || !email || !password) return; + await createUser.mutateAsync({ name, email, password }); + setName(""); + setEmail(""); + setPassword(""); + }; + + return ( +
+ + + 用户列表 + 当前共 {users.data?.total ?? "—"} 位用户 + + + {users.isPending ? ( +
+ 加载中… +
+ ) : users.isError ? ( +
+ 加载失败: {users.error.message} +
+ ) : ( + + + + ID + 名称 + 邮箱 + 状态 + + + + {users.data?.users.length === 0 ? ( + + + 暂无数据 + + + ) : ( + users.data?.users.map((u: User) => ( + + + {u.id.slice(0, 8)}… + + {u.name} + {u.email} + + 活跃 + + + )) + )} + +
+ )} +
+
+ + + + 新建用户 + + +
+
+
+ + setName(e.target.value)} + required + /> +
+
+ + setEmail(e.target.value)} + required + /> +
+
+ + setPassword(e.target.value)} + required + /> +
+
+ +
+ + {createUser.isError && ( + + {createUser.error.message} + + )} +
+ +
+
+
+ ); +} diff --git a/app/web/src/components/UserMenu.tsx b/app/web/src/components/UserMenu.tsx new file mode 100644 index 0000000..dad7cc5 --- /dev/null +++ b/app/web/src/components/UserMenu.tsx @@ -0,0 +1,125 @@ +/** + * 顶栏用户菜单:接 `auth.me` / `auth.logout`,未登录显示登录/注册入口。 + */ + +import type { Locale } from "@app/i18n"; +import { t } from "@app/i18n"; +import { + Avatar, + AvatarFallback, + AvatarImage, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, + toast, +} from "@app/ui"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { createTRPCReactClient, trpc } from "../lib/trpc"; + +const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } }, +}); +const trpcClient = createTRPCReactClient(); + +export interface UserMenuProps { + locale?: Locale | undefined; + name?: string | undefined; + email?: string | undefined; + avatarUrl?: string | undefined; +} + +function home(locale: Locale): string { + return locale === "zh-CN" ? "/zh-CN" : "/en"; +} + +export default function UserMenu({ locale = "zh-CN", avatarUrl }: UserMenuProps) { + return ( + + + + + + ); +} + +function UserMenuInner({ locale, avatarUrl }: { locale: Locale; avatarUrl?: string | undefined }) { + const me = trpc.auth.me.useQuery(); + const logout = trpc.auth.logout.useMutation({ + onSuccess: () => { + toast.success(t(locale, "auth.msg.logoutOk")); + queryClient.clear(); + window.location.reload(); + }, + onError: (err) => toast.error(err.message || t(locale, "auth.err.generic")), + }); + + const base = home(locale); + const user = me.data?.user ?? null; + + if (me.isPending) { + return …; + } + + if (!user) { + return ( + + + {t(locale, "auth.menu.login")} + + + {t(locale, "auth.menu.register")} + + + ); + } + + const name = user.name; + const initials = name.slice(0, 1); + + return ( + + + + + + +
+ {name} + {user.email} +
+
+ + + {t(locale, "auth.menu.dashboard")} + + + {t(locale, "auth.menu.settings")} + + + { + e.preventDefault(); + logout.mutate(); + }} + disabled={logout.isPending} + > + {t(locale, "auth.menu.logout")} + +
+
+ ); +} diff --git a/app/web/src/components/errors/Error403.tsx b/app/web/src/components/errors/Error403.tsx new file mode 100644 index 0000000..d71082d --- /dev/null +++ b/app/web/src/components/errors/Error403.tsx @@ -0,0 +1,47 @@ +/** 403 禁止访问 — 锁形图标;布局与其它错误组件一致。 */ +import { createAppError } from "~/lib/errors"; +import { ErrorShell } from "./ErrorShell"; + +export interface Error403Props { + label: string; + title: string; + description: string; + homeHref: string; + homeLabel: string; +} + +function LockIcon() { + return ( + + ); +} + +export function Error403(props: Error403Props) { + return ( + } + /> + ); +} + +export default Error403; diff --git a/app/web/src/components/errors/Error404.tsx b/app/web/src/components/errors/Error404.tsx new file mode 100644 index 0000000..d854905 --- /dev/null +++ b/app/web/src/components/errors/Error404.tsx @@ -0,0 +1,49 @@ +/** 404 未找到 — 断链/路径图标;布局与其它错误组件一致。 */ +import { createAppError } from "~/lib/errors"; +import { ErrorShell } from "./ErrorShell"; + +export interface Error404Props { + label: string; + title: string; + description: string; + homeHref: string; + homeLabel: string; +} + +function PathBreakIcon() { + return ( + + ); +} + +export function Error404(props: Error404Props) { + return ( + } + /> + ); +} + +export default Error404; diff --git a/app/web/src/components/errors/Error500.tsx b/app/web/src/components/errors/Error500.tsx new file mode 100644 index 0000000..e178e1f --- /dev/null +++ b/app/web/src/components/errors/Error500.tsx @@ -0,0 +1,53 @@ +/** 500 及其它内部错误 — 警示三角图标;布局与其它错误组件一致。 */ +import type { ReactNode } from "react"; +import { createAppError, type AppError } from "~/lib/errors"; +import { ErrorShell } from "./ErrorShell"; + +export interface Error500Props { + label: string; + title: string; + description: string; + homeHref: string; + homeLabel: string; + /** 可选:重试按钮等 */ + action?: ReactNode; + /** 组件级 boundary 传入时保留原始 status/kind */ + error?: AppError; +} + +function AlertTriangleIcon() { + return ( + + ); +} + +export function Error500(props: Error500Props) { + return ( + } + {...(props.action !== undefined ? { action: props.action } : {})} + /> + ); +} + +export default Error500; diff --git a/app/web/src/components/errors/ErrorBoundary.tsx b/app/web/src/components/errors/ErrorBoundary.tsx new file mode 100644 index 0000000..a8a9186 --- /dev/null +++ b/app/web/src/components/errors/ErrorBoundary.tsx @@ -0,0 +1,68 @@ +/** + * 组件级 ErrorBoundary:捕获子树异常 → toAppError → 按 status 渲染 403/404/500。 + * 文案由 i18n 经 props 传入(与路由级错误页同一套 key)。 + */ +import { Component, type ErrorInfo, type ReactNode } from "react"; +import { toAppError, type AppError, type AppErrorStatus } from "~/lib/errors"; +import { Error403 } from "./Error403"; +import { Error404 } from "./Error404"; +import { Error500 } from "./Error500"; + +export interface ErrorTexts { + /** 面板标签,如 ERROR / 系统错误 */ + label: string; + title: string; + description: string; + homeLabel: string; +} + +export interface ErrorBoundaryProps { + children: ReactNode; + homeHref: string; + texts: Record; + /** 可选:错误时回调(埋点等) */ + onError?: (error: AppError, info: ErrorInfo) => void; + /** 自定义 fallback;不传则用内置三组件 */ + fallback?: (error: AppError) => ReactNode; +} + +interface ErrorBoundaryState { + error: AppError | null; +} + +export class ErrorBoundary extends Component { + override state: ErrorBoundaryState = { error: null }; + + static getDerivedStateFromError(error: unknown): ErrorBoundaryState { + return { error: toAppError(error) }; + } + + override componentDidCatch(error: unknown, info: ErrorInfo): void { + const appError = toAppError(error); + this.props.onError?.(appError, info); + if (import.meta.env.DEV) { + console.error("[ErrorBoundary]", appError, info); + } + } + + override render(): ReactNode { + const { error } = this.state; + if (!error) return this.props.children; + if (this.props.fallback) return this.props.fallback(error); + + const texts = this.props.texts[error.status]; + const common = { + label: texts.label, + title: texts.title, + description: texts.description, + homeHref: this.props.homeHref, + homeLabel: texts.homeLabel, + }; + + if (error.status === 403) return ; + if (error.status === 404) return ; + return ; + } +} + +export default ErrorBoundary; diff --git a/app/web/src/components/errors/ErrorShell.tsx b/app/web/src/components/errors/ErrorShell.tsx new file mode 100644 index 0000000..33eb516 --- /dev/null +++ b/app/web/src/components/errors/ErrorShell.tsx @@ -0,0 +1,97 @@ +/** + * 错误展示共享外壳:三套错误组件布局一致,仅图标与文案随 AppError 变化。 + * 可独立 SSR(无 client:*),也可被 ErrorBoundary 在客户端渲染。 + */ +import type { ReactNode } from "react"; +import { cn } from "~/lib/utils"; +import type { AppError } from "~/lib/errors"; + +export interface ErrorShellProps { + error: AppError; + label: string; + title: string; + description: string; + icon: ReactNode; + homeHref: string; + homeLabel: string; + action?: ReactNode; + className?: string; +} + +export function ErrorShell({ + error, + label, + title, + description, + icon, + homeHref, + homeLabel, + action, + className, +}: ErrorShellProps) { + return ( +
+
+ ); +} diff --git a/app/web/src/components/errors/ErrorView.tsx b/app/web/src/components/errors/ErrorView.tsx new file mode 100644 index 0000000..ff1bda5 --- /dev/null +++ b/app/web/src/components/errors/ErrorView.tsx @@ -0,0 +1,48 @@ +/** + * 按 AppError 分发到 403 / 404 / 500 三组件;文案走 i18n prefix。 + * 路由级 .astro 与组件级共用。 + */ +import type { Locale } from "@app/i18n"; +import { t } from "@app/i18n"; +import { errorI18nPrefix, type AppError, type AppErrorStatus } from "~/lib/errors"; +import { Error403 } from "./Error403"; +import { Error404 } from "./Error404"; +import { Error500 } from "./Error500"; + +export interface ErrorViewProps { + error: AppError; + locale: Locale; + homeHref: string; +} + +export function ErrorView({ error, locale, homeHref }: ErrorViewProps) { + const prefix = errorI18nPrefix(error); + const label = t(locale, `${prefix}.label` as never); + const title = t(locale, `${prefix}.title` as never); + const description = t(locale, `${prefix}.desc` as never); + const homeLabel = t(locale, "error.home"); + const common = { label, title, description, homeHref, homeLabel }; + + if (error.status === 403) return ; + if (error.status === 404) return ; + return ; +} + +export function errorTextsFor(locale: Locale): Record< + AppErrorStatus, + { + label: string; + title: string; + description: string; + homeLabel: string; + } +> { + const homeLabel = t(locale, "error.home"); + const of = (kind: "forbidden" | "notFound" | "internal") => ({ + label: t(locale, `error.${kind}.label` as never), + title: t(locale, `error.${kind}.title` as never), + description: t(locale, `error.${kind}.desc` as never), + homeLabel, + }); + return { 403: of("forbidden"), 404: of("notFound"), 500: of("internal") }; +} diff --git a/app/web/src/components/errors/index.ts b/app/web/src/components/errors/index.ts new file mode 100644 index 0000000..5d3557e --- /dev/null +++ b/app/web/src/components/errors/index.ts @@ -0,0 +1,11 @@ +/** 错误组件桶导出。 */ +export { ErrorShell, type ErrorShellProps } from "./ErrorShell"; +export { Error403, type Error403Props } from "./Error403"; +export { Error404, type Error404Props } from "./Error404"; +export { Error500, type Error500Props } from "./Error500"; +export { + ErrorBoundary, + type ErrorBoundaryProps, + type ErrorTexts, +} from "./ErrorBoundary"; +export { ErrorView, errorTextsFor, type ErrorViewProps } from "./ErrorView"; diff --git a/app/web/src/env.d.ts b/app/web/src/env.d.ts new file mode 100644 index 0000000..efa5412 --- /dev/null +++ b/app/web/src/env.d.ts @@ -0,0 +1,12 @@ +/// +/// + +interface ImportMetaEnv { + readonly PUBLIC_API_URL: string; + /** 站点生产域名,如 https://tool.example.com(SEO 规范源) */ + readonly PUBLIC_SITE_URL?: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/app/web/src/layouts/AppLayout.astro b/app/web/src/layouts/AppLayout.astro new file mode 100644 index 0000000..ddab262 --- /dev/null +++ b/app/web/src/layouts/AppLayout.astro @@ -0,0 +1,217 @@ +--- +import "../styles/globals.css"; +import UserMenu from "~/components/UserMenu"; +import ThemeSwitch from "~/components/ThemeSwitch"; +import { + buildDefaultJsonLd, + buildHeadTags, + renderHeadTagsHtml, + toJsonLdScript, + type PageSeoInput, +} from "@app/seo-geo"; +import { COOKIE_LANG, COOKIE_LANG_MAX_AGE, type Locale, t } from "@app/i18n"; +import { getSiteSeo } from "~/lib/seo"; + +export interface Props { + title: string; + locale: Locale; + brand?: string; + nav?: { label: string; href: string; n: string }[]; + switchHref?: string; + switchLabel?: string; + seo?: PageSeoInput; +} + +const { + title, + locale, + brand, + nav: navProp, + switchHref, + switchLabel, + seo, +} = Astro.props; + +const brandName = brand ?? t(locale, "meta.brand"); +const nav = navProp ?? []; + +const siteSeo = getSiteSeo(locale); +const pageSeoInput: PageSeoInput = { + ...(seo ?? {}), + title: seo?.title ?? title, + path: seo?.path ?? Astro.url.pathname, +}; +const headTags = buildHeadTags(siteSeo, pageSeoInput); +const headHtml = renderHeadTagsHtml(headTags); +const jsonLdHtml = toJsonLdScript(buildDefaultJsonLd(siteSeo, pageSeoInput)); +const pageTitle = headTags.find((tTag) => tTag.key === "og:title")?.value ?? title; + +const homeHref = locale === "zh-CN" ? "/zh-CN" : "/en"; +const brandHref = homeHref; +const brandInitial = locale === "zh-CN" ? "模" : "A"; +--- + + + + + + + + + + + + + + + + <Fragment set:html={headHtml} /> + <Fragment set:html={jsonLdHtml} /> + <script is:inline define:vars={{ cookieName: COOKIE_LANG, maxAge: COOKIE_LANG_MAX_AGE, nextLocale: locale === "zh-CN" ? "en" : "zh-CN" }}> + (function () { + function apply(mode) { + var dark = + mode === "dark" || + (mode !== "light" && + window.matchMedia("(prefers-color-scheme: dark)").matches); + document.documentElement.classList.toggle("dark", dark); + document.documentElement.classList.toggle("light", !dark); + document.documentElement.style.colorScheme = dark ? "dark" : "light"; + } + var mode = "system"; + try { + var t = localStorage.getItem("app-theme"); + if (t === "light" || t === "dark" || t === "system") mode = t; + } catch (e) {} + apply(mode); + window + .matchMedia("(prefers-color-scheme: dark)") + .addEventListener("change", function () { + try { + if ((localStorage.getItem("app-theme") || "system") === "system") { + apply("system"); + } + } catch (e) { + apply("system"); + } + }); + + // 语言切换:写偏好 cookie 后由 <a> 跳转(捕获阶段) + document.addEventListener( + "click", + function (e) { + var el = e.target && e.target.closest ? e.target.closest("[data-switch-locale]") : null; + if (!el) return; + var target = el.getAttribute("data-switch-locale") || nextLocale; + document.cookie = + cookieName + "=" + target + "; path=/; max-age=" + maxAge + "; SameSite=Lax"; + }, + true, + ); + })(); + </script> + </head> + <body> + <div aria-hidden="true" class="bg-base pointer-events-none fixed inset-0 z-0"></div> + <div + aria-hidden="true" + class="u-grid-field pointer-events-none fixed inset-0 z-0" + style="opacity: 0.18;" + > + </div> + <div aria-hidden="true" class="bg-glow pointer-events-none fixed inset-0 z-0"></div> + <div aria-hidden="true" class="bg-vignette pointer-events-none fixed inset-0 z-0"></div> + + <aside class="side-rail" aria-label={t(locale, "nav.primary")}> + <a href={brandHref} class="side-rail__brand" aria-label={brandName}> + <span + class="brand-signal flex size-8 items-center justify-center text-[12px] font-bold" + style="background: var(--edge); color: #101110;" + > + {brandInitial} + </span> + <span class="side-rail__brand-text">{brandName}</span> + </a> + <div class="side-rail__rule" aria-hidden="true"></div> + <nav class="side-rail__nav"> + <a href={brandHref} class="side-rail__link is-home"> + <span class="u-mono">00</span> + <span>{t(locale, "nav.home")}</span> + </a> + { + nav.map((item) => ( + <a href={item.href} class="side-rail__link"> + <span class="u-mono">{item.n}</span> + <span>{item.label}</span> + </a> + )) + } + </nav> + <div class="side-rail__rule" aria-hidden="true"></div> + <div class="side-rail__foot"> + <ThemeSwitch client:visible /> + </div> + </aside> + + <div class="site-shell"> + <header class="site-header"> + <div class="site-header__inner"> + <a href={brandHref} class="site-header__brand md:hidden" aria-label={brandName}> + <span + class="brand-signal flex size-7 items-center justify-center text-[11px] font-bold" + style="background: var(--edge); color: #101110;" + > + {brandInitial} + </span> + <span class="text-[14px] font-semibold tracking-[0.04em]">{brandName}</span> + </a> + <nav class="site-header__nav" aria-label={t(locale, "nav.primary")}> + { + nav.map((item) => ( + <a href={item.href} class="u-linelink"> + <span class="u-mono">{item.n}</span> + <span>{item.label}</span> + </a> + )) + } + </nav> + <div class="site-header__actions"> + { + switchHref && ( + <a + href={switchHref} + class="u-linelink" + data-switch-locale={locale === "zh-CN" ? "en" : "zh-CN"} + hreflang={locale === "zh-CN" ? "en" : "zh-CN"} + aria-label={t(locale, "locale.switch")} + > + {switchLabel ?? (locale === "zh-CN" ? "EN" : "中文")} + </a> + ) + } + <span class="md:hidden"> + <ThemeSwitch client:visible /> + </span> + <UserMenu client:visible locale={locale} /> + </div> + </div> + <div class="site-header__dash" aria-hidden="true"></div> + </header> + + <main class="page-main"> + <slot /> + </main> + + <footer class="site-footer"> + <div class="u-dotline" aria-hidden="true"></div> + <div class="site-footer__inner"> + <p class="u-mono">{t(locale, "meta.brand")}</p> + <p class="u-label">{t(locale, "home.footer")}</p> + </div> + </footer> + </div> + </body> +</html> diff --git a/app/web/src/layouts/index.ts b/app/web/src/layouts/index.ts new file mode 100644 index 0000000..01e619e --- /dev/null +++ b/app/web/src/layouts/index.ts @@ -0,0 +1,2 @@ +// 布局入口:AppLayout.astro 由页面直接导入 +export {}; diff --git a/app/web/src/lib/errors.ts b/app/web/src/lib/errors.ts new file mode 100644 index 0000000..1ee0532 --- /dev/null +++ b/app/web/src/lib/errors.ts @@ -0,0 +1,88 @@ +/** + * 统一应用错误对象:检测 / 分发文案与展示组件。 + * 组件级(ErrorBoundary)与路由级(404/403/500 页)共用。 + */ + +export type AppErrorStatus = 403 | 404 | 500; + +export type AppErrorKind = "forbidden" | "notFound" | "internal"; + +export interface AppError { + readonly name: "AppError"; + readonly status: AppErrorStatus; + readonly kind: AppErrorKind; + /** 展示用短码,如 FORBIDDEN / NOT_FOUND / INTERNAL */ + readonly code: string; + readonly message: string; + /** 原始错误(组件级 boundary 捕获时保留) */ + readonly cause?: unknown; +} + +const KIND_BY_STATUS: Record<AppErrorStatus, AppErrorKind> = { + 403: "forbidden", + 404: "notFound", + 500: "internal", +}; + +const CODE_BY_STATUS: Record<AppErrorStatus, string> = { + 403: "FORBIDDEN", + 404: "NOT_FOUND", + 500: "INTERNAL", +}; + +const DEFAULT_MESSAGE: Record<AppErrorStatus, string> = { + 403: "Access denied", + 404: "Not found", + 500: "Internal error", +}; + +export function createAppError( + status: AppErrorStatus, + options?: { message?: string; cause?: unknown }, +): AppError { + const err: AppError = { + name: "AppError", + status, + kind: KIND_BY_STATUS[status], + code: CODE_BY_STATUS[status], + message: options?.message ?? DEFAULT_MESSAGE[status], + }; + if (options && "cause" in options) { + return { ...err, cause: options.cause }; + } + return err; +} + +export function isAppError(value: unknown): value is AppError { + return ( + typeof value === "object" && + value !== null && + (value as { name?: unknown }).name === "AppError" && + typeof (value as { status?: unknown }).status === "number" && + ((value as { status: number }).status === 403 || + (value as { status: number }).status === 404 || + (value as { status: number }).status === 500) + ); +} + +/** 未知异常 → AppError(非 403/404 一律归为 500)。 */ +export function toAppError(value: unknown): AppError { + if (isAppError(value)) return value; + if (value instanceof Error) { + return createAppError(500, { message: value.message, cause: value }); + } + return createAppError(500, { cause: value }); +} + +/** 按 HTTP status / 数字字符串归一到三类之一。 */ +export function statusToAppError(status: number | string): AppError { + const n = typeof status === "string" ? Number.parseInt(status, 10) : status; + if (n === 403) return createAppError(403); + if (n === 404) return createAppError(404); + return createAppError(500); +} + +/** i18n key 前缀:`error.${kind}` → title/desc/label。 */ +export function errorI18nPrefix(error: AppError): `error.${AppErrorKind}` { + return `error.${error.kind}`; +} diff --git a/app/web/src/lib/seo.ts b/app/web/src/lib/seo.ts new file mode 100644 index 0000000..337f96a --- /dev/null +++ b/app/web/src/lib/seo.ts @@ -0,0 +1,63 @@ +/** 站点 SEO 按 locale 构造(唯一配置源仍在此,喂给 @app/seo-geo)。 */ +import type { PageSeoInput, SiteSeoConfig } from "@app/seo-geo"; +import { type Locale, t } from "@app/i18n"; + +const rawSiteUrl = import.meta.env.PUBLIC_SITE_URL?.replace(/\/+$/, ""); +const siteUrl = rawSiteUrl && rawSiteUrl.length > 0 ? rawSiteUrl : "http://localhost:4321"; + +/** 无 locale 的公开路径(basePath);受保护页面不进 llms/sitemap 列表。 */ +const PUBLIC_PATHS = ["/"] as const; + +function pathForLocale(locale: Locale, basePath: string): string { + if (basePath === "/") return locale === "zh-CN" ? "/zh-CN" : "/en"; + return locale === "zh-CN" ? `/zh-CN${basePath}` : `/en${basePath}`; +} + +export function getSiteSeo(locale: Locale): SiteSeoConfig { + const name = t(locale, "meta.brand"); + const isZh = locale === "zh-CN"; + const description = isZh + ? "Astro + tRPC + Drizzle monorepo 应用脚手架,开箱即用的分层基线。" + : "Astro + tRPC + Drizzle monorepo application scaffold with a ready vertical baseline."; + + return { + siteUrl, + name, + description, + locale: isZh ? "zh-CN" : "en", + defaultImage: "/og-default.png", + organization: { name, url: siteUrl }, + llms: { + summary: isZh + ? "这是可复用的应用脚手架模板:Astro SSR + React islands + tRPC + Drizzle/Turso,附带鉴权与用户垂直示例。" + : "Reusable application scaffold: Astro SSR + React islands + tRPC + Drizzle/Turso, with auth and a user vertical example.", + details: [ + isZh + ? "分层:types → models → db → dao → trpc → web。业务实体按 user 切片扩展,禁止跨层。" + : "Layers: types → models → db → dao → trpc → web. Extend domains via the user slice; never skip layers.", + isZh + ? "不绑定具体产品业务;works 等领域逻辑应在业务仓库中添加。" + : "No product domain baked in; add domains such as works in your product repo.", + ], + pages: PUBLIC_PATHS.map((basePath) => ({ + path: pathForLocale(locale, basePath), + title: basePath === "/" ? `${name}${isZh ? "首页" : " home"}` : name, + description, + })), + discouraged: isZh + ? ["/dashboard、/settings 等需登录页面", "/auth/* 等认证页", "/api/* 接口响应"] + : [ + "/dashboard, /settings and other app pages", + "/auth/* and auth pages", + "/api/* responses", + ], + }, + }; +} + +/** 兼容旧引用:默认中文。 */ +export const siteSeo = getSiteSeo("zh-CN"); + +export function pageSeo(input: PageSeoInput): PageSeoInput { + return input; +} diff --git a/app/web/src/lib/theme.ts b/app/web/src/lib/theme.ts new file mode 100644 index 0000000..ae98d78 --- /dev/null +++ b/app/web/src/lib/theme.ts @@ -0,0 +1,45 @@ +export type ThemeMode = "system" | "light" | "dark"; + +const KEY = "app-theme"; + +export function readThemeMode(): ThemeMode { + try { + const v = localStorage.getItem(KEY); + if (v === "light" || v === "dark" || v === "system") return v; + } catch { + /* noop */ + } + return "system"; +} + +export function writeThemeMode(mode: ThemeMode) { + try { + localStorage.setItem(KEY, mode); + } catch { + /* noop */ + } +} + +export function systemPrefersDark(): boolean { + return window.matchMedia("(prefers-color-scheme: dark)").matches; +} + +export function resolveTheme(mode: ThemeMode): "light" | "dark" { + if (mode === "system") return systemPrefersDark() ? "dark" : "light"; + return mode; +} + +export function applyTheme(mode: ThemeMode): "light" | "dark" { + const resolved = resolveTheme(mode); + const root = document.documentElement; + root.classList.toggle("dark", resolved === "dark"); + root.classList.toggle("light", resolved === "light"); + root.style.colorScheme = resolved; + return resolved; +} + +export function watchSystemTheme(onChange: () => void): () => void { + const mq = window.matchMedia("(prefers-color-scheme: dark)"); + mq.addEventListener("change", onChange); + return () => mq.removeEventListener("change", onChange); +} diff --git a/app/web/src/lib/trpc.ts b/app/web/src/lib/trpc.ts new file mode 100644 index 0000000..f973736 --- /dev/null +++ b/app/web/src/lib/trpc.ts @@ -0,0 +1,21 @@ +/** + * 前端 tRPC 客户端:React Query 绑定 + batch HTTP 链到 `/api/trpc`。 + * 类型来自 `@app/trpc` 的 AppRouter,改 procedure 后前端自动跟上。 + */ +import { createTRPCReact } from "@trpc/react-query"; +import { httpBatchLink } from "@trpc/client"; +import type { AppRouter } from "@app/trpc"; + +export const trpc = createTRPCReact<AppRouter>(); + +export function createTRPCReactClient() { + return trpc.createClient({ + links: [ + httpBatchLink({ + url: "/api/trpc", + // 便于服务端日志区分 island 流量 + headers: () => ({ "x-trpc-source": "react-island" }), + }), + ], + }); +} diff --git a/app/web/src/lib/utils.ts b/app/web/src/lib/utils.ts new file mode 100644 index 0000000..95c4963 --- /dev/null +++ b/app/web/src/lib/utils.ts @@ -0,0 +1 @@ +export { cn } from "@app/ui"; diff --git a/app/web/src/middleware.ts b/app/web/src/middleware.ts new file mode 100644 index 0000000..583a4a6 --- /dev/null +++ b/app/web/src/middleware.ts @@ -0,0 +1,84 @@ +import { defineMiddleware } from "astro:middleware"; +import { COOKIE_LANG, localePath, resolvePreferredLocale } from "@app/i18n"; +import { COOKIE_SESSION } from "@app/types"; +import { authService } from "@app/trpc"; + +const SKIP_PREFIX = ["/api", "/_astro", "/favicon", "/robots", "/sitemap", "/llms"]; + +/** 需登录的基础路径(无 locale 前缀)。按需增删。 */ +const PROTECTED_BASES = ["/dashboard", "/settings"] as const; + +const LOCALE_SEGMENTS = new Set(["zh-CN", "en"]); + +function shouldSkip(pathname: string): boolean { + if (pathname !== "/" && pathname.includes(".")) return true; + return SKIP_PREFIX.some((p) => pathname.startsWith(p)); +} + +function basePathOf(pathname: string): string { + const segs = pathname.split("/").filter(Boolean); + const head = segs[0]; + if (head && LOCALE_SEGMENTS.has(head)) { + return `/${segs.slice(1).join("/")}`; + } + // 规范化前的 zh-cn 等在后续 redirect 处理,这里先按原样判断 + if (head && head.toLowerCase() === "zh-cn") { + return `/${segs.slice(1).join("/")}`; + } + return `/${segs.join("/")}`; +} + +function isProtectedPath(pathname: string): boolean { + const base = basePathOf(pathname); + const normalized = base === "/" ? "/" : base.replace(/\/$/, ""); + return PROTECTED_BASES.some((p) => normalized === p || normalized.startsWith(`${p}/`)); +} + +function localeOf(pathname: string): "zh-CN" | "en" { + const head = pathname.split("/").filter(Boolean)[0]; + if (head === "en") return "en"; + return "zh-CN"; +} + +export const onRequest = defineMiddleware(async (context, next) => { + const { request, url, cookies } = context; + const path = url.pathname; + + if (shouldSkip(path)) { + return next(); + } + + // 裸 `/`:cookie → Accept-Language → /zh-CN 或 /en + if (path === "/") { + if (request.method !== "GET") return next(); + const preferred = resolvePreferredLocale( + cookies.get(COOKIE_LANG)?.value, + request.headers.get("accept-language"), + ); + return context.redirect(localePath(preferred, "/")); + } + + // 小写 zh-cn → 规范 zh-CN(保留 rest) + const seg = path.split("/").filter(Boolean)[0]; + if (seg && seg.toLowerCase() === "zh-cn" && seg !== "zh-CN") { + const rest = path.slice(seg.length) || "/"; + return context.redirect(`/zh-CN${rest === "/" ? "" : rest}`); + } + + // 指定路由鉴权:无有效 session 则跳登录 + if (request.method === "GET" && isProtectedPath(path)) { + const token = cookies.get(COOKIE_SESSION)?.value; + const user = await authService.getSessionUser(token); + if (!user) { + const locale = localeOf(path); + const nextParam = encodeURIComponent(path + url.search); + return context.redirect(`${localePath(locale, "/auth/login")}?next=${nextParam}`); + } + } + + const response = await next(); + if (request.method === "GET") { + response.headers.set("Vary", "Accept-Language"); + } + return response; +}); diff --git a/app/web/src/pages/403.astro b/app/web/src/pages/403.astro new file mode 100644 index 0000000..0f0f857 --- /dev/null +++ b/app/web/src/pages/403.astro @@ -0,0 +1,13 @@ +--- +import { COOKIE_LANG, resolvePreferredLocale } from "@app/i18n"; +import ErrorViewPage from "~/views/error.astro"; + +/** 显式 403 页;middleware / 业务可用 rewrite 或 redirect 到此。 */ +const locale = resolvePreferredLocale( + Astro.cookies.get(COOKIE_LANG)?.value, + Astro.request.headers.get("accept-language"), +); +Astro.response.status = 403; +--- + +<ErrorViewPage locale={locale} status={403} /> diff --git a/app/web/src/pages/404.astro b/app/web/src/pages/404.astro new file mode 100644 index 0000000..589a452 --- /dev/null +++ b/app/web/src/pages/404.astro @@ -0,0 +1,13 @@ +--- +import { COOKIE_LANG, resolvePreferredLocale } from "@app/i18n"; +import ErrorViewPage from "~/views/error.astro"; + +// Astro 全局 404:按 cookie / Accept-Language 选 locale(无 [locale] 段) +const locale = resolvePreferredLocale( + Astro.cookies.get(COOKIE_LANG)?.value, + Astro.request.headers.get("accept-language"), +); +Astro.response.status = 404; +--- + +<ErrorViewPage locale={locale} status={404} /> diff --git a/app/web/src/pages/500.astro b/app/web/src/pages/500.astro new file mode 100644 index 0000000..0276c05 --- /dev/null +++ b/app/web/src/pages/500.astro @@ -0,0 +1,12 @@ +--- +import { COOKIE_LANG, resolvePreferredLocale } from "@app/i18n"; +import ErrorViewPage from "~/views/error.astro"; + +const locale = resolvePreferredLocale( + Astro.cookies.get(COOKIE_LANG)?.value, + Astro.request.headers.get("accept-language"), +); +Astro.response.status = 500; +--- + +<ErrorViewPage locale={locale} status={500} /> diff --git a/app/web/src/pages/[locale]/auth/forgot-password.astro b/app/web/src/pages/[locale]/auth/forgot-password.astro new file mode 100644 index 0000000..d973ec8 --- /dev/null +++ b/app/web/src/pages/[locale]/auth/forgot-password.astro @@ -0,0 +1,19 @@ +--- +import { COOKIE_LANG, isLocale, localePath, resolvePreferredLocale, type Locale } from "@app/i18n"; +import AuthView from "~/views/auth.astro"; + +const raw = Astro.params["locale"]; +const normalized = raw === "zh-cn" ? "zh-CN" : raw; + +if (!normalized || !isLocale(normalized)) { + const preferred = resolvePreferredLocale( + Astro.cookies.get(COOKIE_LANG)?.value, + Astro.request.headers.get("accept-language"), + ); + return Astro.redirect(localePath(preferred, "/auth/forgot-password")); +} + +const locale: Locale = normalized; +--- + +<AuthView locale={locale} mode="forgot" /> diff --git a/app/web/src/pages/[locale]/auth/login.astro b/app/web/src/pages/[locale]/auth/login.astro new file mode 100644 index 0000000..6730a53 --- /dev/null +++ b/app/web/src/pages/[locale]/auth/login.astro @@ -0,0 +1,20 @@ +--- +import { COOKIE_LANG, isLocale, localePath, resolvePreferredLocale, type Locale } from "@app/i18n"; +import AuthView from "~/views/auth.astro"; + +const raw = Astro.params["locale"]; +const normalized = raw === "zh-cn" ? "zh-CN" : raw; + +if (!normalized || !isLocale(normalized)) { + const preferred = resolvePreferredLocale( + Astro.cookies.get(COOKIE_LANG)?.value, + Astro.request.headers.get("accept-language"), + ); + return Astro.redirect(localePath(preferred, "/auth/login")); +} + +const locale: Locale = normalized; +const next = Astro.url.searchParams.get("next"); +--- + +<AuthView locale={locale} mode="login" next={next ?? undefined} /> diff --git a/app/web/src/pages/[locale]/auth/register.astro b/app/web/src/pages/[locale]/auth/register.astro new file mode 100644 index 0000000..ff5845d --- /dev/null +++ b/app/web/src/pages/[locale]/auth/register.astro @@ -0,0 +1,19 @@ +--- +import { COOKIE_LANG, isLocale, localePath, resolvePreferredLocale, type Locale } from "@app/i18n"; +import AuthView from "~/views/auth.astro"; + +const raw = Astro.params["locale"]; +const normalized = raw === "zh-cn" ? "zh-CN" : raw; + +if (!normalized || !isLocale(normalized)) { + const preferred = resolvePreferredLocale( + Astro.cookies.get(COOKIE_LANG)?.value, + Astro.request.headers.get("accept-language"), + ); + return Astro.redirect(localePath(preferred, "/auth/register")); +} + +const locale: Locale = normalized; +--- + +<AuthView locale={locale} mode="register" /> diff --git a/app/web/src/pages/[locale]/auth/reset-password.astro b/app/web/src/pages/[locale]/auth/reset-password.astro new file mode 100644 index 0000000..a1cc183 --- /dev/null +++ b/app/web/src/pages/[locale]/auth/reset-password.astro @@ -0,0 +1,20 @@ +--- +import { COOKIE_LANG, isLocale, localePath, resolvePreferredLocale, type Locale } from "@app/i18n"; +import AuthView from "~/views/auth.astro"; + +const raw = Astro.params["locale"]; +const normalized = raw === "zh-cn" ? "zh-CN" : raw; + +if (!normalized || !isLocale(normalized)) { + const preferred = resolvePreferredLocale( + Astro.cookies.get(COOKIE_LANG)?.value, + Astro.request.headers.get("accept-language"), + ); + return Astro.redirect(localePath(preferred, "/auth/reset-password")); +} + +const locale: Locale = normalized; +const token = Astro.url.searchParams.get("token") ?? undefined; +--- + +<AuthView locale={locale} mode="reset" token={token} /> diff --git a/app/web/src/pages/[locale]/dashboard.astro b/app/web/src/pages/[locale]/dashboard.astro new file mode 100644 index 0000000..51ab521 --- /dev/null +++ b/app/web/src/pages/[locale]/dashboard.astro @@ -0,0 +1,19 @@ +--- +import { COOKIE_LANG, isLocale, localePath, resolvePreferredLocale, type Locale } from "@app/i18n"; +import Dashboard from "~/views/dashboard.astro"; + +const raw = Astro.params["locale"]; +const normalized = raw === "zh-cn" ? "zh-CN" : raw; + +if (!normalized || !isLocale(normalized)) { + const preferred = resolvePreferredLocale( + Astro.cookies.get(COOKIE_LANG)?.value, + Astro.request.headers.get("accept-language"), + ); + return Astro.redirect(localePath(preferred, "/dashboard")); +} + +const locale: Locale = normalized; +--- + +<Dashboard locale={locale} /> diff --git a/app/web/src/pages/[locale]/index.astro b/app/web/src/pages/[locale]/index.astro new file mode 100644 index 0000000..a0f0ea3 --- /dev/null +++ b/app/web/src/pages/[locale]/index.astro @@ -0,0 +1,20 @@ +--- +import { COOKIE_LANG, isLocale, localePath, resolvePreferredLocale, type Locale } from "@app/i18n"; +import Home from "~/views/home.astro"; + +const raw = Astro.params["locale"]; +const normalized = raw === "zh-cn" ? "zh-CN" : raw; + +if (!normalized || !isLocale(normalized)) { + // 非法/缺失前缀:按 cookie + Accept-Language 自动匹配语言 + const preferred = resolvePreferredLocale( + Astro.cookies.get(COOKIE_LANG)?.value, + Astro.request.headers.get("accept-language"), + ); + return Astro.redirect(localePath(preferred, "/")); +} + +const locale: Locale = normalized; +--- + +<Home locale={locale} /> diff --git a/app/web/src/pages/[locale]/settings.astro b/app/web/src/pages/[locale]/settings.astro new file mode 100644 index 0000000..bf4b041 --- /dev/null +++ b/app/web/src/pages/[locale]/settings.astro @@ -0,0 +1,19 @@ +--- +import { COOKIE_LANG, isLocale, localePath, resolvePreferredLocale, type Locale } from "@app/i18n"; +import AuthView from "~/views/auth.astro"; + +const raw = Astro.params["locale"]; +const normalized = raw === "zh-cn" ? "zh-CN" : raw; + +if (!normalized || !isLocale(normalized)) { + const preferred = resolvePreferredLocale( + Astro.cookies.get(COOKIE_LANG)?.value, + Astro.request.headers.get("accept-language"), + ); + return Astro.redirect(localePath(preferred, "/settings")); +} + +const locale: Locale = normalized; +--- + +<AuthView locale={locale} mode="change" /> diff --git a/app/web/src/pages/api/trpc/[...trpc].ts b/app/web/src/pages/api/trpc/[...trpc].ts new file mode 100644 index 0000000..1942eec --- /dev/null +++ b/app/web/src/pages/api/trpc/[...trpc].ts @@ -0,0 +1,51 @@ +/** + * Astro 内嵌 tRPC HTTP 适配:React islands 与外部客户端统一走 `/api/trpc/*`。 + * 与 `@app/trpc` 的 `server.ts` 共用 appRouter,二选一部署即可。 + * 从 cookie 解析 session,并把 procedure 写入的 Set-Cookie 合并进响应。 + */ +import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; +import type { APIRoute } from "astro"; +import { appRouter } from "@app/trpc"; +import { COOKIE_SESSION } from "@app/types"; + +export const prerender = false; + +function parseCookies(header: string | null): Record<string, string> { + const out: Record<string, string> = {}; + if (!header) return out; + for (const part of header.split(";")) { + const eq = part.indexOf("="); + if (eq < 0) continue; + const key = part.slice(0, eq).trim(); + const val = part.slice(eq + 1).trim(); + if (key) out[key] = decodeURIComponent(val); + } + return out; +} + +export const ALL: APIRoute = async (ctx) => { + const setCookies: string[] = []; + const cookies = parseCookies(ctx.request.headers.get("cookie")); + + const response = await fetchRequestHandler({ + endpoint: "/api/trpc", + req: ctx.request, + router: appRouter, + createContext: () => ({ + sessionToken: cookies[COOKIE_SESSION], + setCookie: (value: string) => { + setCookies.push(value); + }, + }), + }); + + if (setCookies.length === 0) return response; + + const headers = new Headers(response.headers); + for (const c of setCookies) headers.append("Set-Cookie", c); + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers, + }); +}; diff --git a/app/web/src/pages/api/users.ts b/app/web/src/pages/api/users.ts new file mode 100644 index 0000000..45a18c4 --- /dev/null +++ b/app/web/src/pages/api/users.ts @@ -0,0 +1,22 @@ +/** 演示用 REST 转发:同进程内改写为 tRPC user.list,勿在此堆业务。 */ +import type { APIRoute } from "astro"; + +export const prerender = false; + +export const GET: APIRoute = async ({ request }) => { + // 内部转发到 tRPC handler(同进程内,避免跨网络) + const url = new URL(request.url); + url.pathname = "/api/trpc/user.list"; + const req = new Request(url.toString(), { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ json: { limit: 20, offset: 0 } }), + }); + + // 调用同一个 Astro 进程的 tRPC endpoint + const res = await fetch(req); + const data = await res.json(); + return new Response(JSON.stringify(data), { + headers: { "Content-Type": "application/json" }, + }); +}; diff --git a/app/web/src/pages/llms-full.txt.ts b/app/web/src/pages/llms-full.txt.ts new file mode 100644 index 0000000..1405007 --- /dev/null +++ b/app/web/src/pages/llms-full.txt.ts @@ -0,0 +1,63 @@ +/** GEO 端点:llms-full.txt(中文默认 + 英文节选)。 */ +import type { APIRoute } from "astro"; +import { buildLlmsFullTxt, buildLlmsTxt } from "@app/seo-geo"; +import { type Locale, t } from "@app/i18n"; +import { getSiteSeo } from "~/lib/seo"; + +export const prerender = false; + +function buildFor(locale: Locale): string { + const siteSeo = getSiteSeo(locale); + const isZh = locale === "zh-CN"; + + if (!isZh) { + return buildLlmsTxt(siteSeo); + } + + return buildLlmsFullTxt(siteSeo, { + title: `${t(locale, "meta.brand")} · scaffold index`, + sections: [ + { + heading: t(locale, "home.stackTitle"), + path: "/zh-CN", + summary: t(locale, "home.stackBody"), + points: [ + "Astro 7 SSR + React 19 islands", + "tRPC v11 + Drizzle ORM + Turso/libSQL", + "Tailwind CSS 4 + Biome + Yarn 4 workspaces + Turbo", + ], + }, + { + heading: t(locale, "home.layerTitle"), + path: "/zh-CN", + summary: t(locale, "home.layerBody"), + points: [ + "types → models → db → dao → trpc → web", + "user / auth 为垂直示例,可替换为业务实体", + ], + }, + ], + faqs: [ + { + question: "这个模板包含业务逻辑吗?", + answer: "不包含产品业务。仅保留鉴权与 user 垂直示例,用于演示分层接线;具体业务应在下游仓库添加。", + }, + { + question: "如何新增业务实体?", + answer: "按 user 切片扩展:@app/types 加 schema → @app/models 建表 → @app/dao 加查询 → @app/trpc 加 service/router → @app/web 挂页面。", + }, + ], + }); +} + +export const GET: APIRoute = ({ url }) => { + const locale: Locale = url.pathname.startsWith("/en") ? "en" : "zh-CN"; + const body = buildFor(locale); + return new Response(body, { + headers: { + "Content-Type": "text/plain; charset=utf-8", + "Cache-Control": "public, max-age=3600", + Vary: "Accept-Language", + }, + }); +}; diff --git a/app/web/src/pages/llms.txt.ts b/app/web/src/pages/llms.txt.ts new file mode 100644 index 0000000..7098d7c --- /dev/null +++ b/app/web/src/pages/llms.txt.ts @@ -0,0 +1,19 @@ +/** GEO 端点:llms.txt。`?lang=en` 或路径无 zh 时输出英文(端点本身无 locale 前缀)。 */ +import type { APIRoute } from "astro"; +import { buildLlmsTxt } from "@app/seo-geo"; +import { getSiteSeo } from "~/lib/seo"; + +export const prerender = false; + +export const GET: APIRoute = ({ url }) => { + const lang = url.searchParams.get("lang"); + const locale = lang === "en" ? "en" : "zh-CN"; + const body = buildLlmsTxt(getSiteSeo(locale)); + return new Response(body, { + headers: { + "Content-Type": "text/plain; charset=utf-8", + "Cache-Control": "public, max-age=3600", + Vary: "Accept-Language", + }, + }); +}; diff --git a/app/web/src/pages/robots.txt.ts b/app/web/src/pages/robots.txt.ts new file mode 100644 index 0000000..28e9b92 --- /dev/null +++ b/app/web/src/pages/robots.txt.ts @@ -0,0 +1,16 @@ +/** SEO 端点:输出 robots.txt(默认允许抓取 + 指向 sitemap)。 */ +import type { APIRoute } from "astro"; +import { buildDefaultRobotsInput, buildRobotsTxt } from "@app/seo-geo"; +import { siteSeo } from "~/lib/seo"; + +export const prerender = false; + +export const GET: APIRoute = () => { + const body = buildRobotsTxt(buildDefaultRobotsInput(siteSeo)); + return new Response(body, { + headers: { + "Content-Type": "text/plain; charset=utf-8", + "Cache-Control": "public, max-age=3600", + }, + }); +}; diff --git a/app/web/src/pages/sitemap.xml.ts b/app/web/src/pages/sitemap.xml.ts new file mode 100644 index 0000000..64c8bc3 --- /dev/null +++ b/app/web/src/pages/sitemap.xml.ts @@ -0,0 +1,47 @@ +/** SEO 站点地图:双语 path + hreflang alternates。 */ +import type { APIRoute } from "astro"; +import { buildSitemapXml, type SitemapEntry } from "@app/seo-geo"; +import { alternatePath, LOCALES } from "@app/i18n"; +import { siteSeo } from "~/lib/seo"; + +export const prerender = false; + +function entryFor( + basePath: string, + opts: { changefreq: SitemapEntry["changefreq"]; priority: number }, +): SitemapEntry { + const alternates: NonNullable<SitemapEntry["alternates"]> = [ + ...LOCALES.map((locale) => ({ + href: alternatePath(basePath, locale), + hreflang: locale as string, + })), + { href: alternatePath(basePath, "zh-CN"), hreflang: "x-default" }, + ]; + return { + url: alternatePath(basePath, "zh-CN"), + changefreq: opts.changefreq, + priority: opts.priority, + alternates, + }; +} + +export const GET: APIRoute = async () => { + const entries: SitemapEntry[] = [ + entryFor("/", { changefreq: "weekly", priority: 1.0 }), + { + url: "/en", + changefreq: "weekly", + priority: 1.0, + alternates: entryFor("/", { changefreq: "weekly", priority: 1.0 }).alternates, + }, + ]; + + const xml = await buildSitemapXml(siteSeo, entries); + return new Response(xml, { + headers: { + "Content-Type": "application/xml; charset=utf-8", + "Cache-Control": "public, max-age=3600", + Vary: "Accept-Language", + }, + }); +}; diff --git a/app/web/src/styles/globals.css b/app/web/src/styles/globals.css new file mode 100644 index 0000000..e5f940f --- /dev/null +++ b/app/web/src/styles/globals.css @@ -0,0 +1,1154 @@ +@import "tailwindcss"; +@import "@app/design-tokens/tokens.css"; + +*, +*::before, +*::after { + box-sizing: border-box; + border-color: var(--line); +} + +html { + font-family: var(--font-sans); + scroll-behavior: smooth; + overflow-x: hidden; +} + +body { + margin: 0; + min-height: 100vh; + overflow-x: hidden; + background: var(--paper); + color: var(--ink); + font-variant-numeric: tabular-nums; + line-height: 1.55; + -webkit-font-smoothing: antialiased; +} + +a, +button, +label, +summary, +select { + cursor: pointer; +} + +:focus-visible { + outline: 1px solid var(--edge); + outline-offset: 3px; +} + +::selection { + background: var(--edge); + color: #101110; +} + +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background: var(--line-strong); + border-radius: 0; +} +::-webkit-scrollbar-thumb:hover { + background: var(--edge); +} + +.u-mono { + font-family: var(--font-mono); + font-variant-numeric: tabular-nums; +} + +.u-label { + font-family: var(--font-mono); + font-size: 10px; + font-weight: 500; + letter-spacing: 0.2em; + text-transform: uppercase; + color: var(--ink-2); +} + +.u-label-edge { + color: var(--edge-as-text); +} + +.u-edge-bar { + display: block; + width: 48px; + height: 2px; + background: var(--edge); +} + +.u-break { + overflow-wrap: anywhere; + word-break: break-word; +} + +.u-hazard { + background: var(--hazard); +} + +.u-hazard-edge { + background: var(--hazard-edge); +} + +.u-grid-field { + background-image: + linear-gradient( + to right, + color-mix(in srgb, var(--line) 55%, transparent) 1px, + transparent 1px + ), + linear-gradient( + to bottom, + color-mix(in srgb, var(--line) 55%, transparent) 1px, + transparent 1px + ); + background-size: 56px 56px; +} + +html.has-reveal .u-reveal { + opacity: 0; + transform: translate3d(0, 20px, 0); + transition: + opacity 0.65s cubic-bezier(0.22, 1, 0.36, 1) var(--reveal-delay, 0ms), + transform 0.65s cubic-bezier(0.22, 1, 0.36, 1) var(--reveal-delay, 0ms); + will-change: transform, opacity; +} +html.has-reveal .u-reveal.is-in { + opacity: 1; + transform: none; +} +/* island 内部不依赖 JS 加类:入场后直接显示,避免 hydration 冲突 */ +astro-island .u-reveal, +html.has-reveal astro-island .u-reveal { + opacity: 1; + transform: none; +} + +.u-parallax { + will-change: transform; + transform: translate3d(0, 0, 0); +} + +/* —— 连续滚动(cyclemon 式自由滚 + 分层视差,不做硬锁) —— */ +.fp-root { + height: calc(100dvh - 3.5rem); + overflow-y: auto; + overflow-x: hidden; + scroll-snap-type: none; + scroll-behavior: auto; + position: relative; + z-index: 10; +} +.fp-section { + min-height: calc(100dvh - 3.5rem); + scroll-snap-align: none; + scroll-snap-stop: normal; + display: flex; + flex-direction: column; + justify-content: center; + padding: 3.5rem 0 4rem; + position: relative; +} +.fp-section > .fp-inner { + width: 100%; + max-width: 72rem; + margin: 0 auto; + padding: 0 1.5rem; +} +@media (min-width: 768px) { + .fp-section > .fp-inner { + padding: 0 2rem; + } +} +.fp-dots { + position: fixed; + right: 16px; + top: 50%; + transform: translateY(-50%); + z-index: 60; + display: flex; + flex-direction: column; + gap: 12px; +} +.fp-dots button { + width: 6px; + height: 6px; + padding: 0; + border: none; + border-radius: 50%; + background: var(--line-strong); + color: transparent; + overflow: hidden; + transition: + background 0.2s ease, + transform 0.2s ease, + box-shadow 0.2s ease; +} +.fp-dots button.is-active { + background: var(--edge); + transform: scale(1.35); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--edge) 22%, transparent); +} +.fp-dots button:hover { + background: var(--ink-2); +} + +/* —— muuuuu 式点线 / 虚线分隔 —— */ +.u-dotline { + height: 1px; + width: 100%; + background-image: linear-gradient( + to right, + color-mix(in srgb, var(--ink-3) 55%, transparent) 0 4px, + transparent 4px 8px + ); + background-size: 8px 1px; + background-repeat: repeat-x; + opacity: 0.45; +} + +.u-dashline { + height: 0; + border-bottom: 1px dashed color-mix(in srgb, var(--line-strong) 70%, transparent); +} + +/* hover 下划线从左展开(.c-linelink) */ +.u-linelink { + position: relative; + display: inline-flex; + align-items: baseline; + gap: 0.4rem; + color: var(--ink-2); + text-decoration: none; + transition: color 0.2s ease; +} +.u-linelink::after { + content: ""; + position: absolute; + left: 0; + bottom: -2px; + width: 100%; + height: 1px; + background: var(--edge); + transform: scaleX(0); + transform-origin: left center; + transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1); +} +.u-linelink:hover, +.u-linelink:focus-visible { + color: var(--ink); +} +.u-linelink:hover::after, +.u-linelink:focus-visible::after { + transform: scaleX(1); +} +.u-linelink .u-mono { + font-size: 10px; + letter-spacing: 0.12em; + color: var(--ink-3); +} +.u-linelink:hover .u-mono { + color: var(--edge-as-text); +} + +/* —— 细边圆角按钮(.c-btn) —— */ +.u-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.45rem; + min-height: 36px; + padding: 0 14px; + border: 1px solid var(--line); + border-radius: 5px; + background: var(--panel); + color: var(--ink-2); + font-size: 13px; + line-height: 1; + text-decoration: none; + transition: + border-color 0.2s ease, + color 0.2s ease, + background 0.2s ease, + box-shadow 0.2s ease; +} +.u-btn:hover, +.u-btn:focus-visible { + border-color: color-mix(in srgb, var(--edge) 70%, var(--line)); + color: var(--ink); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--edge) 12%, transparent); +} +.u-btn--edge { + background: var(--edge); + border-color: var(--edge); + color: #101110; + font-weight: 600; +} +.u-btn--edge:hover { + background: var(--edge-deep); + border-color: var(--edge-deep); + color: #101110; + box-shadow: 0 0 0 2px color-mix(in srgb, var(--edge) 25%, transparent); +} +.u-btn--ghost { + background: transparent; +} + +/* —— 卡片:5px 圆角 + 轻阴影 —— */ +.u-card { + background: var(--panel); + border: 1px solid var(--line); + border-radius: 5px; + box-shadow: 0 7px 12px color-mix(in srgb, #000 18%, transparent); + transition: + transform 0.28s cubic-bezier(0.22, 1, 0.36, 1), + box-shadow 0.28s ease, + border-color 0.2s ease; +} +.u-card:hover, +.u-card:focus-visible { + transform: translateY(-3px); + border-color: color-mix(in srgb, var(--edge) 55%, var(--line)); + box-shadow: + 0 7px 12px color-mix(in srgb, #000 20%, transparent), + 0 0 0 1px color-mix(in srgb, var(--edge) 25%, transparent); +} +.light .u-card { + box-shadow: 0 7px 12px rgb(0 0 0 / 0.12); +} +.light .u-card:hover { + box-shadow: + 0 10px 18px rgb(0 0 0 / 0.14), + 0 0 0 1px color-mix(in srgb, var(--edge) 35%, transparent); +} + +/* 左侧信号条(类别色) */ +.u-card__spine { + position: absolute; + left: 0; + top: 10px; + bottom: 10px; + width: 2px; + border-radius: 0 2px 2px 0; + opacity: 0.85; + transition: + width 0.2s ease, + opacity 0.2s ease; +} +.u-card:hover .u-card__spine { + width: 3px; + opacity: 1; +} + +.u-ghost { + font-family: var(--font-sans); + font-weight: 700; + line-height: 0.8; + color: transparent; + -webkit-text-stroke: 1px color-mix(in srgb, var(--ink-3) 55%, transparent); + user-select: none; + letter-spacing: 0.08em; +} + +/* —— 左侧固定竖栏 —— */ +.side-rail { + display: none; +} +@media (min-width: 1024px) { + .side-rail { + position: fixed; + left: 1.25rem; + top: 1.25rem; + bottom: 1.25rem; + z-index: 50; + width: 5.75rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.75rem; + padding: 1rem 0.5rem; + border: 1px solid var(--line); + border-radius: 5px; + background: color-mix(in srgb, var(--panel) 92%, transparent); + backdrop-filter: blur(16px); + box-shadow: 0 7px 12px color-mix(in srgb, #000 16%, transparent); + } +} + +.side-rail__brand { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.45rem; + text-decoration: none; + color: var(--ink); +} +.side-rail__brand-text { + font-size: 12px; + font-weight: 600; + letter-spacing: 0.08em; +} +.side-rail__rule { + width: 60%; + height: 0; + border-top: 1px dashed color-mix(in srgb, var(--line-strong) 75%, transparent); +} +.side-rail__nav { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 0.35rem; + width: 100%; + flex: 1; + min-height: 0; +} +.side-rail__link { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.15rem; + padding: 0.55rem 0.25rem; + border-radius: 5px; + border: 1px solid transparent; + color: var(--ink-2); + font-size: 12px; + text-decoration: none; + transition: + background 0.2s ease, + color 0.2s ease, + border-color 0.2s ease; +} +.side-rail__link .u-mono { + font-size: 10px; + letter-spacing: 0.14em; + color: var(--ink-3); +} +.side-rail__link:hover, +.side-rail__link:focus-visible { + background: color-mix(in srgb, var(--edge) 10%, transparent); + border-color: color-mix(in srgb, var(--edge) 35%, var(--line)); + color: var(--ink); +} +.side-rail__link:hover .u-mono { + color: var(--edge-as-text); +} +.side-rail__foot { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.55rem; + width: 100%; +} +.side-rail__foot .u-btn, +.side-rail__foot button { + width: 100%; + padding: 0 6px; + font-size: 10px; + letter-spacing: 0.08em; +} +.side-rail__live { + font-size: 9px; + letter-spacing: 0.16em; + color: var(--ink-3); +} + +/* —— 内容壳:桌面左让竖栏、右留边 —— */ +.site-shell { + position: relative; + z-index: 10; + display: flex; + flex-direction: column; + min-height: 100dvh; +} +@media (min-width: 1024px) { + .site-shell { + padding-left: 8.5rem; + padding-right: 3.5rem; + } +} +@media (min-width: 1280px) { + .site-shell { + padding-left: 10.5rem; + padding-right: 5rem; + } +} +.site-shell--page { + height: 100dvh; + min-height: 100dvh; + max-height: 100dvh; + overflow: hidden; +} +.site-shell--page .fp-root { + flex: 1; + min-height: 0; + height: auto; +} + +.page-main { + flex: 1; + width: 100%; + max-width: 72rem; + margin: 0 auto; + padding: 1.5rem 1.25rem 3rem; +} +@media (min-width: 768px) { + .page-main { + padding: 2rem 2rem 4rem; + } +} + +/* —— 顶栏:细线 + 滚动后 fixed 感 blur + 虚线底 —— */ +.site-header { + position: sticky; + top: 0; + z-index: 50; + background: color-mix(in srgb, var(--paper) 36%, transparent); + transition: + background 0.25s ease, + backdrop-filter 0.25s ease, + box-shadow 0.25s ease; +} +.site-header__inner { + display: flex; + align-items: center; + gap: 1.5rem; + height: 3.5rem; + max-width: 72rem; + margin: 0 auto; + padding: 0 1.25rem; +} +@media (min-width: 768px) { + .site-header__inner { + padding: 0 2rem; + } +} +.site-header__brand { + display: flex; + align-items: center; + gap: 0.55rem; + text-decoration: none; + color: var(--ink); +} +.site-header__nav { + display: none; + align-items: center; + gap: 1.35rem; + font-size: 13px; +} +@media (min-width: 768px) { + .site-header__nav { + display: flex; + } +} +/* 桌面已有侧栏时,顶栏可隐藏重复分类导航,仅保留品牌/操作 */ +@media (min-width: 1024px) { + .site-header__nav { + display: none; + } +} +.site-header__actions { + margin-left: auto; + display: flex; + align-items: center; + gap: 0.75rem; +} +.site-header__live { + display: none; + font-size: 10px; + letter-spacing: 0.18em; + color: var(--ink-3); +} +@media (min-width: 1024px) { + .site-header__live { + display: inline; + } +} +.site-header__dash { + height: 0; + border-bottom: 1px dashed transparent; + transition: border-color 0.25s ease; +} +html.is-scrolled .site-header { + background: color-mix(in srgb, var(--paper) 72%, transparent); + backdrop-filter: blur(60px); + -webkit-backdrop-filter: blur(60px); +} +html.is-scrolled .site-header .site-header__dash { + border-bottom-color: color-mix(in srgb, var(--line-strong) 65%, transparent); +} +html.is-scrolled .site-header .brand-signal { + box-shadow: 0 0 14px color-mix(in srgb, var(--edge) 35%, transparent); +} + +.site-footer { + position: relative; + margin-top: auto; +} +.site-footer__inner { + display: flex; + flex-direction: column; + gap: 0.5rem; + max-width: 72rem; + margin: 0 auto; + padding: 1.5rem 1.25rem 2rem; + font-size: 12px; + color: var(--ink-2); +} +@media (min-width: 768px) { + .site-footer__inner { + flex-direction: row; + justify-content: space-between; + padding: 1.75rem 2rem 2.25rem; + } +} + +/* —— 机能:细织带 / 刻度(克制) —— */ +.u-tech-tape { + display: inline-flex; + align-items: center; + height: 22px; + padding: 0 8px; + font-size: 9px; + letter-spacing: 0.16em; + color: var(--ink-2); + border: 1px solid var(--line); + border-radius: 5px; + text-transform: uppercase; + background: color-mix(in srgb, var(--panel) 80%, transparent); +} + +.u-tech-ruler { + height: 8px; + width: min(240px, 50%); + background: repeating-linear-gradient(90deg, var(--line-strong) 0 1px, transparent 1px 12px); + opacity: 0.55; +} + +.u-tech-panel { + position: relative; +} +.u-tech-panel::before { + content: ""; + position: absolute; + left: 0; + top: 16px; + bottom: 16px; + width: 2px; + background: var(--edge); + opacity: 0.85; +} + +/* 兼容旧类名:贴纸感裁切降级为轻圆角 */ +.u-cut, +.u-cut-lg, +.u-cut-x { + clip-path: none; +} +.u-cut, +.u-cut-lg { + border-radius: 5px; +} +.u-cut-x { + border-radius: 5px; +} +.u-frame::before, +.u-frame::after { + display: none; +} + +/* 扫描高光 → 极轻掠过(可关) */ +.u-scan::after { + background: linear-gradient( + 115deg, + transparent 45%, + color-mix(in srgb, var(--edge) 6%, transparent) 50%, + transparent 55% + ); +} + +/* —— 背景层(无扫描动画) —— */ +.bg-base { + background: var(--paper); +} + +.bg-glow { + background: + radial-gradient( + ellipse 60% 45% at 18% 12%, + color-mix(in srgb, var(--edge) 7%, transparent), + transparent 55% + ), + radial-gradient( + ellipse 50% 40% at 85% 75%, + color-mix(in srgb, var(--sig-cyan) 5%, transparent), + transparent 50% + ); + opacity: 0.75; +} + +.bg-vignette { + background: radial-gradient( + ellipse at center, + transparent 55%, + color-mix(in srgb, var(--paper) 38%, transparent) 100% + ); +} + +/* 全页背景等高线:铺满视口、灰系、低存在感 */ +.contour-field { + z-index: 0; + width: 100vw; + height: 100dvh; + opacity: 0.7; +} +.light .contour-field { + opacity: 0.42; +} + +@keyframes rise-in { + from { + opacity: 0; + transform: translateY(14px); + } + to { + opacity: 1; + transform: none; + } +} + +@keyframes signal-pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.4; + } +} + +@keyframes scroll-tip { + 0%, + 100% { + transform: translateY(0); + opacity: 0.35; + } + 50% { + transform: translateY(5px); + opacity: 0.9; + } +} + +.u-rise { + animation: rise-in 0.65s cubic-bezier(0.22, 1, 0.36, 1) both; +} +.u-pulse { + animation: signal-pulse 2s ease-in-out infinite; +} +.u-scroll-tip { + animation: scroll-tip 2s ease-in-out infinite; +} + +/* —— 沉浸首页:文档流滚动(Lenis)+ 大型星图 —— */ +html.has-motion { + scroll-behavior: auto; +} + +.site-shell--immersive { + min-height: 100dvh; +} + +.imm-root { + position: relative; + z-index: 10; + width: 100%; +} + +.imm-hero { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + min-height: calc(100dvh - 3.5rem); + max-width: 84rem; + margin: 0 auto; + padding: 2.5rem 1.25rem 3rem; + align-items: center; +} +@media (min-width: 768px) { + .imm-hero { + padding: 3rem 2rem 3.5rem; + } +} +@media (min-width: 1100px) { + .imm-hero { + grid-template-columns: minmax(280px, 0.9fr) minmax(320px, 1.15fr); + gap: 1.5rem; + } +} + +.imm-hero__title { + margin: 0; + font-size: clamp(40px, 7vw, 72px); + font-weight: 900; + line-height: 1.05; + letter-spacing: -0.03em; +} +.imm-hero__line { + display: block; + overflow: hidden; +} +.imm-hero__line > span { + display: inline-block; +} + +.imm-stat { + border: 1px solid var(--line); + border-radius: 5px; + background: color-mix(in srgb, var(--panel) 80%, transparent); + padding: 0.7rem 0.75rem; +} +.imm-stat strong { + display: block; + margin-top: 0.35rem; + font-size: 1.35rem; + font-weight: 600; + color: var(--ink); +} + +.imm-hero__stage { + min-width: 0; +} + +.command-orbit { + position: relative; + border: 1px solid var(--line); + border-radius: 5px; + background: color-mix(in srgb, var(--sink) 72%, transparent); + box-shadow: 0 7px 12px color-mix(in srgb, #000 18%, transparent); + overflow: hidden; + perspective: 900px; +} +.command-orbit::before { + content: ""; + position: absolute; + inset: 0; + background: var(--hazard); + height: 4px; + opacity: 0.85; + pointer-events: none; + z-index: 2; +} +.command-orbit__svg { + display: block; + width: 100%; + height: auto; +} +.command-orbit__tilt { + transform-style: preserve-3d; + will-change: transform; + transform: perspective(1100px) translate3d(0, 0, 0); +} +.command-orbit .orbit-stroke { + fill: none; + stroke: color-mix(in srgb, var(--ink) 42%, var(--line-strong)); +} +.command-orbit .orbit-stroke-edge { + fill: none; + stroke: var(--edge); +} +.command-orbit .orbit-fill-panel { + fill: color-mix(in srgb, var(--sink) 80%, var(--ink) 8%); + stroke: color-mix(in srgb, var(--ink) 35%, var(--line-strong)); +} +.command-orbit .orbit-text-ink { + fill: var(--ink); +} +.command-orbit .orbit-text-dim { + fill: var(--ink-2); +} +.command-orbit .orbit-text-edge { + fill: var(--edge-as-text); +} +.command-orbit .orbit-spin-slow, +.command-orbit .orbit-spin-rev, +.command-orbit .orbit-sweep { + transform-box: view-box; + transform-origin: 600px 390px; +} +.command-orbit .orbit-spin-slow { + animation: orbit-cw 48s linear infinite; +} +.command-orbit .orbit-spin-rev { + animation: orbit-ccw 36s linear infinite; +} +.command-orbit .orbit-sweep { + animation: orbit-cw 7.5s linear infinite; +} +.command-orbit .orbit-pulse { + animation: orbit-pulse 1.8s ease-in-out infinite; +} +@keyframes orbit-cw { + to { + transform: rotate(360deg); + } +} +@keyframes orbit-ccw { + to { + transform: rotate(-360deg); + } +} +@keyframes orbit-pulse { + 0%, + 100% { + opacity: 0.55; + } + 50% { + opacity: 0.18; + } +} +@media (prefers-reduced-motion: reduce) { + .command-orbit .orbit-spin-slow, + .command-orbit .orbit-spin-rev, + .command-orbit .orbit-sweep, + .command-orbit .orbit-pulse { + animation: none; + } +} +.command-orbit__legend { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; + padding: 0.65rem 0.75rem; + border-top: 1px dashed color-mix(in srgb, var(--line-strong) 70%, transparent); + background: color-mix(in srgb, var(--panel) 88%, transparent); +} +.command-orbit__chip { + display: inline-flex; + align-items: center; + gap: 0.35rem; + min-height: 28px; + padding: 0 0.55rem; + border: 1px solid var(--line); + border-radius: 5px; + font-size: 12px; + color: var(--ink-2); + text-decoration: none; + transition: + color 0.2s ease, + border-color 0.2s ease, + background 0.2s ease; +} +.command-orbit__chip i { + width: 6px; + height: 6px; + border-radius: 1px; + display: inline-block; +} +.command-orbit__chip:hover { + color: var(--ink); + border-color: color-mix(in srgb, var(--edge) 55%, var(--line)); + background: color-mix(in srgb, var(--edge) 8%, transparent); +} +.command-orbit__hint { + margin-left: auto; + font-size: 10px; + letter-spacing: 0.12em; + color: var(--ink-3); +} +.orbit-node { + transition: opacity 0.2s ease; +} + +.imm-band { + max-width: 84rem; + margin: 0 auto; + padding: 1rem 1.25rem 3rem; +} +@media (min-width: 768px) { + .imm-band { + padding: 1rem 2rem 4rem; + } +} +.imm-band__inner { + width: 100%; +} + +.imm-marquee { + overflow: hidden; + border-block: 1px solid var(--line); + background: color-mix(in srgb, var(--panel) 70%, transparent); + margin-bottom: 2rem; +} +.imm-marquee__track { + display: flex; + width: max-content; + gap: 1.75rem; + padding: 0.7rem 0; + animation: marquee-x 42s linear infinite; +} +.imm-marquee__item { + display: inline-flex; + align-items: center; + gap: 0.55rem; + font-size: 11px; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--ink-3); + white-space: nowrap; +} +.imm-marquee__item i { + width: 5px; + height: 5px; + background: var(--edge); + border-radius: 1px; + opacity: 0.7; +} +@keyframes marquee-x { + from { + transform: translate3d(0, 0, 0); + } + to { + transform: translate3d(-50%, 0, 0); + } +} + +.imm-section { + max-width: 84rem; + margin: 0 auto; + padding: 3.5rem 1.25rem; +} +@media (min-width: 768px) { + .imm-section { + padding: 5rem 2rem; + } +} +.imm-section__title { + margin: 0; + font-size: clamp(32px, 5vw, 48px); + font-weight: 900; + letter-spacing: -0.03em; + line-height: 1.1; +} +.imm-section__head { + margin-bottom: 2rem; +} +.imm-grid { + display: grid; + grid-template-columns: 1fr; + gap: 1rem; +} +@media (min-width: 640px) { + .imm-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} +@media (min-width: 1024px) { + .imm-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} +@media (min-width: 1280px) { + .imm-grid { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } +} + +.imm-section--split { + display: grid; + grid-template-columns: 1fr; + gap: 2.5rem; +} +@media (min-width: 960px) { + .imm-section--split { + grid-template-columns: minmax(260px, 0.9fr) minmax(300px, 1.1fr); + gap: 3.5rem; + align-items: start; + } +} + +.imm-manifesto { + list-style: none; + margin: 0; + padding: 0; + display: grid; + gap: 0.65rem; + counter-reset: none; +} +.imm-manifesto__item { + display: grid; + grid-template-columns: 3rem 1fr; + gap: 0.75rem; + align-items: start; + padding: 0.85rem 0.9rem; + border: 1px solid var(--line); + border-radius: 5px; + background: color-mix(in srgb, var(--panel) 75%, transparent); +} +.imm-manifesto__n { + font-size: 12px; + letter-spacing: 0.14em; + color: var(--edge-as-text); + padding-top: 0.15rem; +} + +.imm-end { + padding-bottom: 4rem; +} +.imm-footer { + max-width: 84rem; + margin: 0 auto; + padding: 0 1.25rem 2rem; +} +@media (min-width: 768px) { + .imm-footer { + padding: 0 2rem 2.5rem; + } +} +.imm-footer__inner { + display: flex; + flex-direction: column; + gap: 0.45rem; + padding-top: 1.25rem; + font-size: 12px; + color: var(--ink-2); +} +@media (min-width: 768px) { + .imm-footer__inner { + flex-direction: row; + justify-content: space-between; + align-items: center; + } +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + .imm-marquee__track { + animation: none; + } + .fp-root { + scroll-behavior: auto; + scroll-snap-type: y proximity; + } + *, + *::before, + *::after { + animation-duration: 0.01ms; + animation-iteration-count: 1; + transition-duration: 0.01ms; + } + .u-reveal { + opacity: 1; + transform: none; + } + .u-parallax { + transform: none; + } +} diff --git a/app/web/src/views/auth.astro b/app/web/src/views/auth.astro new file mode 100644 index 0000000..5d3b9f2 --- /dev/null +++ b/app/web/src/views/auth.astro @@ -0,0 +1,41 @@ +--- +import { alternatePath, type Locale, t } from "@app/i18n"; +import { Toaster } from "@app/ui"; +import AuthForm from "~/components/AuthForm"; +import AppLayout from "~/layouts/AppLayout.astro"; + +export interface Props { + locale: Locale; + mode: "login" | "register" | "forgot" | "reset" | "change"; + next?: string | undefined; + token?: string | undefined; +} + +const { locale, mode, next, token } = Astro.props; +const other: Locale = locale === "zh-CN" ? "en" : "zh-CN"; + +const titleKey = + mode === "login" + ? "auth.login.title" + : mode === "register" + ? "auth.register.title" + : mode === "forgot" + ? "auth.forgot.title" + : mode === "reset" + ? "auth.reset.title" + : "auth.settings.title"; +--- + +<AppLayout + title={`${t(locale, titleKey as never)} · ${t(locale, "meta.brand")}`} + locale={locale} + nav={[]} + switchHref={alternatePath(Astro.url.pathname, other)} + switchLabel={t(other, `locale.${other}` as never)} + seo={{ title: `${t(locale, titleKey as never)} · ${t(locale, "meta.brand")}`, path: Astro.url.pathname, robots: ["noindex", "nofollow"] }} +> + <div class="flex min-h-[60vh] items-center justify-center px-4 py-16"> + <AuthForm client:load locale={locale} mode={mode} next={next} defaultToken={token} /> + </div> + <Toaster client:load /> +</AppLayout> diff --git a/app/web/src/views/dashboard.astro b/app/web/src/views/dashboard.astro new file mode 100644 index 0000000..13f7a56 --- /dev/null +++ b/app/web/src/views/dashboard.astro @@ -0,0 +1,41 @@ +--- +import { alternatePath, type Locale, t } from "@app/i18n"; +import { Toaster } from "@app/ui"; +import UserList from "~/components/UserList"; +import AppLayout from "~/layouts/AppLayout.astro"; + +export interface Props { + locale: Locale; +} + +const { locale } = Astro.props; +const other: Locale = locale === "zh-CN" ? "en" : "zh-CN"; +--- + +<AppLayout + title={t(locale, "dashboard.title")} + locale={locale} + nav={[]} + switchHref={alternatePath(Astro.url.pathname, other)} + switchLabel={t(other, `locale.${other}` as never)} + seo={{ + title: `${t(locale, "dashboard.title")} · ${t(locale, "meta.brand")}`, + path: Astro.url.pathname, + robots: ["noindex", "nofollow"], + }} +> + <div class="space-y-10 py-8"> + <section class="u-reveal"> + <p class="u-label u-label-edge">{t(locale, "dashboard.label")}</p> + <h2 class="mt-2 text-[32px] font-black tracking-tight">{t(locale, "dashboard.heading")}</h2> + <span class="u-edge-bar mt-4"></span> + <p class="mt-4 max-w-2xl text-[14px] leading-relaxed" style="color: var(--ink-2);"> + {t(locale, "dashboard.desc")} + </p> + <div class="u-card mt-8 p-6"> + <UserList client:visible /> + </div> + </section> + </div> + <Toaster client:load /> +</AppLayout> diff --git a/app/web/src/views/error.astro b/app/web/src/views/error.astro new file mode 100644 index 0000000..f596432 --- /dev/null +++ b/app/web/src/views/error.astro @@ -0,0 +1,35 @@ +--- +import { alternatePath, type Locale, t } from "@app/i18n"; +import { ErrorView } from "~/components/errors/ErrorView"; +import { createAppError, type AppErrorStatus } from "~/lib/errors"; +import AppLayout from "~/layouts/AppLayout.astro"; + +export interface Props { + locale: Locale; + status: AppErrorStatus; +} + +const { locale, status } = Astro.props; +const other: Locale = locale === "zh-CN" ? "en" : "zh-CN"; +const error = createAppError(status); +const homeHref = locale === "zh-CN" ? "/zh-CN" : "/en"; +const titleKey = `error.${error.kind}.title` as never; +const pageTitle = t(locale, titleKey); +--- + +<AppLayout + title={`${pageTitle} · ${t(locale, "meta.brand")}`} + locale={locale} + nav={[]} + switchHref={alternatePath(Astro.url.pathname, other)} + switchLabel={t(other, `locale.${other}` as never)} + seo={{ + title: `${pageTitle} · ${t(locale, "meta.brand")}`, + path: Astro.url.pathname, + robots: ["noindex", "nofollow"], + }} +> + <div class="flex min-h-[55vh] items-center justify-center px-4 py-16"> + <ErrorView error={error} locale={locale} homeHref={homeHref} /> + </div> +</AppLayout> diff --git a/app/web/src/views/home.astro b/app/web/src/views/home.astro new file mode 100644 index 0000000..9bbbcee --- /dev/null +++ b/app/web/src/views/home.astro @@ -0,0 +1,55 @@ +--- +import { alternatePath, type Locale, t } from "@app/i18n"; +import AppLayout from "~/layouts/AppLayout.astro"; + +export interface Props { + locale: Locale; +} + +const { locale } = Astro.props; +const tr = (key: Parameters<typeof t>[1]) => t(locale, key); +const otherLocale: Locale = locale === "zh-CN" ? "en" : "zh-CN"; +const switchHref = alternatePath(Astro.url.pathname, otherLocale); +const dashHref = locale === "zh-CN" ? "/zh-CN/dashboard" : "/en/dashboard"; +const loginHref = locale === "zh-CN" ? "/zh-CN/auth/login" : "/en/auth/login"; +--- + +<AppLayout + title={tr("home.title")} + locale={locale} + nav={[]} + switchHref={switchHref} + switchLabel={t(otherLocale, `locale.${otherLocale}` as never)} +> + <section class="space-y-10 py-10"> + <div class="u-reveal"> + <p class="u-label u-label-edge">{tr("meta.tagline")}</p> + <h1 class="mt-3 text-[40px] font-black tracking-tight leading-[1.05]"> + {tr("home.heroLine1")}<br />{tr("home.heroLine2")} + </h1> + <span class="u-edge-bar mt-5"></span> + <p class="mt-5 max-w-xl text-[14px] leading-relaxed" style="color: var(--ink-2);"> + {tr("home.subtitle")} + </p> + <div class="mt-8 flex flex-wrap gap-3"> + <a href={dashHref} class="u-btn u-btn--edge">{tr("home.ctaDashboard")}</a> + <a href={loginHref} class="u-btn u-btn--ghost">{tr("home.ctaLogin")}</a> + </div> + </div> + + <div class="grid gap-6 md:grid-cols-2"> + <section class="u-card p-6 u-reveal"> + <p class="u-label">{tr("home.stackTitle")}</p> + <p class="mt-3 text-[14px] leading-relaxed" style="color: var(--ink-2);"> + {tr("home.stackBody")} + </p> + </section> + <section class="u-card p-6 u-reveal"> + <p class="u-label">{tr("home.layerTitle")}</p> + <p class="mt-3 text-[14px] leading-relaxed" style="color: var(--ink-2);"> + {tr("home.layerBody")} + </p> + </section> + </div> + </section> +</AppLayout> diff --git a/app/web/tsconfig.json b/app/web/tsconfig.json new file mode 100644 index 0000000..62e95bc --- /dev/null +++ b/app/web/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "composite": false, + "jsx": "react-jsx", + "jsxImportSource": "react", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "paths": { + "@app/i18n": ["../../packages/i18n/src/index.ts"], + "@app/trpc": ["../../packages/trpc/src/index.ts"], + "@app/types": ["../../packages/types/src/index.ts"], + "@app/db": ["../../packages/db/src/index.ts"], + "@app/ui": ["../../packages/ui/src/index.ts"], + "@/*": ["./src/*"], + "~/*": ["./src/*"] + } + }, + "include": ["src", ".astro/types.d.ts"] +} diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..526f064 --- /dev/null +++ b/biome.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.5.14/schema.json", + "assist": { + "actions": { + "source": { + "organizeImports": "off" + } + } + }, + "linter": { + "enabled": true, + "rules": { + "preset": "recommended", + "complexity": { + "useLiteralKeys": "off" + }, + "correctness": { + "noUnusedImports": "warn", + "noUnusedVariables": "warn" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 100 + }, + "css": { + "parser": { + "tailwindDirectives": true + } + }, + "files": { + "includes": [ + "**", + "!**/storybook-static", + "!**/dist", + "!**/.turbo", + "!**/node_modules", + "!**/*.astro" + ] + } +} diff --git a/commitlint.config.mjs b/commitlint.config.mjs new file mode 100644 index 0000000..529a119 --- /dev/null +++ b/commitlint.config.mjs @@ -0,0 +1,26 @@ +/** @type {import('@commitlint/types').UserConfig} */ +export default { + extends: ["@commitlint/config-conventional"], + rules: { + "type-enum": [ + 2, + "always", + [ + "feat", + "fix", + "docs", + "style", + "refactor", + "perf", + "test", + "build", + "ci", + "chore", + "revert", + ], + ], + "subject-case": [2, "never", ["start-case", "pascal-case", "upper-case"]], + "header-max-length": [2, "always", 100], + "body-max-line-length": [2, "always", 120], + }, +}; diff --git a/docs/superpowers/specs/2026-09-24-i18n-design.md b/docs/superpowers/specs/2026-09-24-i18n-design.md new file mode 100644 index 0000000..f194192 --- /dev/null +++ b/docs/superpowers/specs/2026-09-24-i18n-design.md @@ -0,0 +1,92 @@ +# i18n 设计 — 方案 A(zh-CN + en · 路径前缀) + +日期:2026-09-24 +状态:已批准(用户选 A;追加 `/` 系统语言检测跳转) + +## 目标 + +- 中文:`/zh-CN`、`/zh-CN/dashboard`(**中文也带前缀,跳 `/zh-CN`,不留在裸 `/`)** +- 英文:`/en`、`/en/dashboard` +- 访问 **`/`** 时:有 `app-lang` cookie 按 cookie 跳 `/zh-CN` 或 `/en`;否则按 `Accept-Language` → 中文 `/zh-CN`、英文 `/en` +- 用户手动切换语言写入 cookie,优先于系统语言 +- 文案集中在 `@app/i18n`;Astro / React 共用同一套静态 messages(无 i18next、无 Context 强绑) + +## 非目标 + +- 不改 tRPC / DAO / DB +- 不引入 i18next / astro-i18next +- 第一阶段不做复数 ICU、后端按 locale 存文案 + +## 包结构 + +``` +packages/i18n/ + package.json # @app/dao 风格:纯 TS,无运行时重依赖 + src/ + locales.ts # LOCALES、DEFAULT_LOCALE、Locale 类型 + cookie.ts # COOKIE_LANG = "app-lang" + accept-language.ts # parsePreferredLocale(header) + messages/ + zh-CN.ts # 扁平命名空间对象(权威 key 源) + en.ts # 必须 keyof zh-CN + t.ts # t(locale, key, params?) 简单 {name} 插值 + index.ts + AGENTS.md +``` + +- `type MessageKey = keyof typeof zhCN` +- `t` 缺 key 编译期失败(en 与 zh 同构) +- 无 IO、无 env;与 seo-geo 同为纯函数包 + +## 路由与 Middleware(@app/web) + +``` +astro.config.ts + i18n: { + defaultLocale: "zh-CN", + locales: ["zh-CN", "en"], + routing: { prefixDefaultLocale: true, redirectToDefaultLocale: false } + } + +src/middleware.ts + GET 且 path === "/" + cookie app-lang=en → redirect /en + cookie app-lang=zh-CN → redirect /zh-CN + 无 cookie: + Accept-Language 偏好 en → /en + 否则(含 zh) → /zh-CN + 响应 Vary: Accept-Language + +src/pages/ + [locale]/index.astro | dashboard.astro # 单套动态前缀(locale 校验) + views/home.astro | dashboard.astro # 双语共用 +``` + +不再使用 `pages/zh-CN` / `pages/en` 双目录;也不依赖 Astro 文件式 i18n 双份路由。 + +语言切换:链接到对侧 URL + `document.cookie = app-lang=...; path=/; max-age=...` +(island `LocaleSwitch` 或 header 内联均可) + +## 文案分层 + +| 层 | 做法 | +|----|------| +| Astro 布局/页面 | `t(locale, "nav.home")` | +| React island | props 传入 `locale`,组件内 `t(locale, ...)` 或传入已解析文案 | +| tools 目录 | `category` 改为稳定 id(`text\|code\|dev\|net\|image`),显示名走 i18n | +| SEO | `siteSeo` 按 locale 构造;sitemap 每 URL 填 `alternates`(`zh-CN` ↔ `en`) | + +## SEO + +- `<html lang>`:`zh-CN` / `en` +- hreflang:`x-default` → `/...`;`zh-CN` → `/...`;`en` → `/en/...` +- `lib/seo.ts` 提供 `getSiteSeo(locale)`,避免写死单 locale +- llms.txt / sitemap 至少覆盖双语文档首页(工具页路径可后补 en 副本) + +## 验收 + +1. `Accept-Language: en` 访问 `/` → 302 `/en` +2. `Accept-Language: zh-CN` 访问 `/` → 200 中文 +3. cookie `app-lang=en` 访问 `/` → 302 `/en`(覆盖系统语言) +4. `/en` 与 `/` 文案、`lang`、导航一致切换 +5. `yarn typecheck` / `yarn build` 通过 diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 0000000..0d3605b --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,9 @@ +commit-msg: + jobs: + - name: commitlint + run: yarn exec commitlint --edit {1} + +pre-push: + jobs: + - name: verify + run: yarn turbo run typecheck lint --filter='./packages/*' diff --git a/package.json b/package.json new file mode 100644 index 0000000..b5fd7b8 --- /dev/null +++ b/package.json @@ -0,0 +1,46 @@ +{ + "name": "app-template", + "private": true, + "workspaces": [ + "packages/*", + "app/*" + ], + "scripts": { + "prepare": "lefthook install", + "dev": "turbo run dev", + "dev:web": "turbo run dev --filter=@app/web", + "dev:api": "turbo run dev --filter=@app/trpc", + "storybook": "yarn workspace @app/ui storybook", + "build": "turbo run build", + "build:web": "turbo run build --filter=@app/web...", + "db:generate": "turbo run db:generate", + "db:push": "turbo run db:push", + "db:studio": "turbo run db:studio", + "typecheck": "turbo run typecheck", + "lint": "turbo run lint", + "clean": "turbo run clean", + "format": "turbo run format", + "_dev": "yarn workspace @app/web dev", + "_dev:api": "yarn workspace @app/trpc dev", + "_build": "yarn workspace @app/trpc build && yarn workspace @app/web build", + "_typecheck": "yarn workspace @app/types run typecheck && yarn workspace @app/db run typecheck && yarn workspace @app/trpc run typecheck && yarn workspace @app/web run typecheck", + "_lint": "yarn workspace @app/types run lint && yarn workspace @app/db run lint && yarn workspace @app/trpc run lint && yarn workspace @app/web run lint" + }, + "packageManager": "yarn@4.17.1", + "devDependencies": { + "@astrojs/check": "^0.9.10", + "@biomejs/biome": "^2.1.1", + "@commitlint/cli": "^21.2.3", + "@commitlint/config-conventional": "^21.2.3", + "@emnapi/runtime": "^1.11.3", + "@storybook/addon-a11y": "^9.1.10", + "@storybook/addon-docs": "^9.1.10", + "@storybook/react": "^9.1.10", + "@storybook/react-vite": "^9.1.10", + "@typescript/typescript6": "^6.0.2", + "lefthook": "^2.1.14", + "storybook": "^9.1.10", + "turbo": "^2.11.2", + "typescript": "^7.0.2" + } +} diff --git a/packages/adsense/AGENTS.md b/packages/adsense/AGENTS.md new file mode 100644 index 0000000..ee1f5e2 --- /dev/null +++ b/packages/adsense/AGENTS.md @@ -0,0 +1,42 @@ +# AGENTS.md — @app/adsense + +## 职责 + +Google AdSense 接入:**幂等脚本加载** + React 广告位组件。与业务/框架解耦,可被 Astro island 或纯 React 使用。 + +## 结构 + +``` +src/ + loader.ts # loadAdSense / pushAdSense + types.ts # AdSensePushCommand + Window.adsbygoogle + react/AdSlot.tsx # <ins class="adsbygoogle"> 组件 + index.ts # 核心导出 + react/index.ts # React 入口(exports: "@app/adsense/react") +``` + +## 导出 + +- `@app/adsense`:`loadAdSense`、`pushAdSense`、类型 +- `@app/adsense/react`:`AdSlot` + +## 行为要点 + +- `loadAdSense(adClient)`:仅浏览器生效;SSR no-op。同一 `adClient` 复用 Promise;页面已有脚本会识别复用;**不支持同时加载多个发布商 ID**。 +- `pushAdSense()`:向 `window.adsbygoogle` 队列 push,渲染已挂载的 `<ins>`。 +- `AdSlot` 属性用 `adSlot` / `adClient`(刻意避开 Astro 的 `slot` / `client:*` 保留字)。挂载后自动 load + push,按 `adClient#adSlot` 去重。 + +## 修改指南 + +- 保持加载幂等;失败时重置 `scriptPromise` 以便重试(见 `loader.ts`)。 +- 不要在本包读 env 或写死 publisher ID——由调用方传入 `adClient`。 +- SSR 安全:凡触碰 `document` / `window` 先判 `typeof`。 +- 新广告格式优先扩展 `AdSlot` props(`format` / `layout` / `layoutKey`…),不要让调用方手写 `<ins>`。 + +## 命令 + +```bash +yarn workspace @app/adsense typecheck +yarn workspace @app/adsense lint +yarn workspace @app/adsense build +``` diff --git a/packages/adsense/package.json b/packages/adsense/package.json new file mode 100644 index 0000000..9891daf --- /dev/null +++ b/packages/adsense/package.json @@ -0,0 +1,34 @@ +{ + "name": "@app/adsense", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./react": { + "types": "./dist/react/index.d.ts", + "import": "./dist/react/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc --project tsconfig.build.json", + "typecheck": "tsc --noEmit", + "lint": "biome check ." + }, + "peerDependencies": { + "react": ">=18" + }, + "devDependencies": { + "@types/react": "^19.1.4", + "react": "^19.1.0", + "typescript": "^7.0.2" + } +} diff --git a/packages/adsense/src/index.ts b/packages/adsense/src/index.ts new file mode 100644 index 0000000..b6ae1c3 --- /dev/null +++ b/packages/adsense/src/index.ts @@ -0,0 +1,3 @@ +/** `@app/adsense` 核心入口:脚本加载 + 队列命令(React 组件走 `@app/adsense/react`)。 */ +export { loadAdSense, pushAdSense } from "./loader.js"; +export type { AdSensePushCommand } from "./types.js"; diff --git a/packages/adsense/src/loader.ts b/packages/adsense/src/loader.ts new file mode 100644 index 0000000..f03201b --- /dev/null +++ b/packages/adsense/src/loader.ts @@ -0,0 +1,70 @@ +import type { AdSensePushCommand } from "./types.js"; + +const SCRIPT_MARKER = "adsbygoogle.js"; + +let scriptPromise: Promise<void> | null = null; +let scriptClient: string | null = null; + +function findExistingScript(adClient: string): HTMLScriptElement | null { + for (const el of Array.from(document.scripts)) { + if (el.src.includes(SCRIPT_MARKER) && el.src.includes(adClient)) { + return el; + } + } + return null; +} + +/** + * 加载 Google AdSense 官方脚本(幂等)。 + * - 仅浏览器环境生效,SSR 中为 no-op + * - 同一 adClient 复用同一 Promise;已在页面中的脚本会被识别并跳过 + */ +export function loadAdSense(adClient: string): Promise<void> { + if (typeof document === "undefined") { + return Promise.resolve(); + } + if (scriptPromise) { + if (scriptClient === adClient) { + return scriptPromise; + } + return Promise.reject( + new Error( + `[adsense] 脚本已按 client=${scriptClient} 加载,不支持同时加载多个发布商 ID`, + ), + ); + } + + const existing = findExistingScript(adClient); + if (existing) { + scriptClient = adClient; + scriptPromise = Promise.resolve(); + return scriptPromise; + } + + scriptClient = adClient; + scriptPromise = new Promise<void>((resolve, reject) => { + const script = document.createElement("script"); + script.async = true; + script.crossOrigin = "anonymous"; + script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${encodeURIComponent(adClient)}`; + script.onload = () => resolve(); + script.onerror = () => { + // 失败必须重置状态,否则后续调用会一直复用 rejected Promise 无法重试 + scriptPromise = null; + scriptClient = null; + script.remove(); + reject(new Error("[adsense] AdSense 脚本加载失败")); + }; + document.head.appendChild(script); + }); + return scriptPromise; +} + +/** 向队列 push 一条命令,触发对已挂载 <ins class="adsbygoogle"> 的渲染。 */ +export function pushAdSense(command: AdSensePushCommand = {}): void { + if (typeof window === "undefined") { + return; + } + window.adsbygoogle ??= []; + window.adsbygoogle.push(command); +} diff --git a/packages/adsense/src/react/AdSlot.tsx b/packages/adsense/src/react/AdSlot.tsx new file mode 100644 index 0000000..0913aab --- /dev/null +++ b/packages/adsense/src/react/AdSlot.tsx @@ -0,0 +1,76 @@ +import { useEffect, useRef, type CSSProperties } from "react"; +import { loadAdSense, pushAdSense } from "../loader.js"; + +export interface AdSlotProps { + /** 广告单元 ID(data-ad-slot) */ + adSlot: string; + /** 发布商 ID(data-ad-client,形如 ca-pub-xxxxxxxx) */ + adClient: string; + /** data-ad-format,默认 auto */ + format?: string | undefined; + /** data-ad-layout */ + layout?: string | undefined; + /** data-ad-layout-key */ + layoutKey?: string | undefined; + /** data-full-width-responsive,未传时不输出该属性 */ + fullWidthResponsive?: boolean | undefined; + className?: string | undefined; + style?: CSSProperties | undefined; +} + +/** + * AdSense 广告位组件:渲染 <ins class="adsbygoogle">, + * 挂载后按需加载官方脚本并 push 渲染命令。 + * + * 注意:属性名刻意避开 Astro 的 `slot` / `client:*` 保留字, + * 在 Astro island 中可直接透传。 + */ +export function AdSlot({ + adSlot, + adClient, + format = "auto", + layout, + layoutKey, + fullWidthResponsive, + className, + style, +}: AdSlotProps) { + const pushedKeyRef = useRef<string | null>(null); + + // 按 adClient#adSlot 去重 push;StrictMode 双调用与依赖重跑都安全 + useEffect(() => { + let cancelled = false; + const key = `${adClient}#${adSlot}`; + + loadAdSense(adClient) + .then(() => { + if (cancelled || pushedKeyRef.current === key) { + return; + } + pushedKeyRef.current = key; + pushAdSense(); + }) + .catch((err: unknown) => { + console.error("[adsense]", err); + }); + + return () => { + cancelled = true; + }; + }, [adClient, adSlot]); + + return ( + <ins + className={className ? `adsbygoogle ${className}` : "adsbygoogle"} + style={{ display: "block", ...style }} + data-ad-client={adClient} + data-ad-slot={adSlot} + data-ad-format={format} + data-ad-layout={layout} + data-ad-layout-key={layoutKey} + data-full-width-responsive={ + fullWidthResponsive === undefined ? undefined : String(fullWidthResponsive) + } + /> + ); +} diff --git a/packages/adsense/src/react/index.ts b/packages/adsense/src/react/index.ts new file mode 100644 index 0000000..70883ba --- /dev/null +++ b/packages/adsense/src/react/index.ts @@ -0,0 +1,2 @@ +/** `@app/adsense/react` 入口:给 Astro island / 纯 React 用的广告位组件。 */ +export { AdSlot, type AdSlotProps } from "./AdSlot.js"; diff --git a/packages/adsense/src/types.ts b/packages/adsense/src/types.ts new file mode 100644 index 0000000..af7d748 --- /dev/null +++ b/packages/adsense/src/types.ts @@ -0,0 +1,11 @@ +/** 向 AdSense 队列 push 的命令参数(一般空对象即可,由 <ins> 上的 data-* 决定广告位)。 */ +export interface AdSensePushCommand { + [key: string]: unknown; +} + +declare global { + interface Window { + /** AdSense 命令队列;官方脚本接管前可能不存在。 */ + adsbygoogle?: unknown[]; + } +} diff --git a/packages/adsense/tsconfig.build.json b/packages/adsense/tsconfig.build.json new file mode 100644 index 0000000..c4a19de --- /dev/null +++ b/packages/adsense/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "./dist", + "rootDir": "./src", + "composite": true + }, + "include": ["src"] +} diff --git a/packages/adsense/tsconfig.json b/packages/adsense/tsconfig.json new file mode 100644 index 0000000..55e1fd7 --- /dev/null +++ b/packages/adsense/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "jsx": "react-jsx", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "paths": {} + }, + "include": ["src"] +} diff --git a/packages/dao/AGENTS.md b/packages/dao/AGENTS.md new file mode 100644 index 0000000..0e6288c --- /dev/null +++ b/packages/dao/AGENTS.md @@ -0,0 +1,39 @@ +# AGENTS.md — @app/dao + +## 职责 + +**数据访问层(DAO)**:封装 Drizzle 查询(`userDao` / `sessionDao` / `passwordResetDao`)。service 只调本包,不直接 `db.select`。不含业务规则、HTTP/tRPC。 + +## 结构 + +``` +src/ + index.ts # 出口:daos + 行类型 + users.ts # userDao + sessions.ts # sessionDao + password-resets.ts # passwordResetDao +``` + +## 依赖 + +- `@app/db`:`db` 单例(连接) +- `@app/models`:表定义与 `$infer*` 行类型 + +## 约定 + +- 一表一个 dao 对象;方法名 `list` / `getById` / `create` / `update` / `delete`。 +- `exactOptionalPropertyTypes`:update 入参用 `Partial<Omit<Row, "id" | "createdAt" | "updatedAt">>`。 +- `noUncheckedIndexedAccess`:`returning()[0]` 判空后抛错或 `?? null`。 +- 不读 env、不建 client;环境变量校验在 `@app/db`。 + +## 修改指南 + +- 加查询:新建或扩展对应 `*.ts`,在 `index.ts` 导出。 +- 新表:先改 `@app/models` → `yarn db:generate`/`db:push` → 本包加 dao。 + +## 命令 + +```bash +yarn workspace @app/dao typecheck +yarn workspace @app/dao build +``` diff --git a/packages/dao/package.json b/packages/dao/package.json new file mode 100644 index 0000000..1741cbe --- /dev/null +++ b/packages/dao/package.json @@ -0,0 +1,31 @@ +{ + "name": "@app/dao", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc --project tsconfig.build.json", + "typecheck": "tsc --noEmit", + "lint": "biome check ." + }, + "dependencies": { + "@app/db": "workspace:*", + "@app/models": "workspace:*", + "drizzle-orm": "^0.45.3" + }, + "devDependencies": { + "@types/node": "^22.15.29", + "typescript": "^7.0.2" + } +} diff --git a/packages/dao/src/index.ts b/packages/dao/src/index.ts new file mode 100644 index 0000000..338e53b --- /dev/null +++ b/packages/dao/src/index.ts @@ -0,0 +1,7 @@ +/** `@app/dao`:数据访问层出口。SQL 只写在本包,service 从这里引入。 */ +export { userDao } from "./users.js"; +export type { UserRow, ListUsersParams } from "./users.js"; +export { sessionDao } from "./sessions.js"; +export type { SessionRow } from "./sessions.js"; +export { passwordResetDao } from "./password-resets.js"; +export type { PasswordResetRow } from "./password-resets.js"; diff --git a/packages/dao/src/password-resets.ts b/packages/dao/src/password-resets.ts new file mode 100644 index 0000000..d730ca8 --- /dev/null +++ b/packages/dao/src/password-resets.ts @@ -0,0 +1,38 @@ +import { eq } from "drizzle-orm"; +import { passwordResets } from "@app/models"; +import { db } from "@app/db"; + +export type PasswordResetRow = typeof passwordResets.$inferSelect; + +export const passwordResetDao = { + async getByToken(token: string): Promise<PasswordResetRow | null> { + const [row] = await db + .select() + .from(passwordResets) + .where(eq(passwordResets.token, token)) + .limit(1); + return row ?? null; + }, + + async create(data: { + userId: string; + token: string; + expiresAt: Date; + }): Promise<PasswordResetRow> { + const rows = await db.insert(passwordResets).values(data).returning(); + const row = rows[0]; + if (!row) throw new Error("Failed to create password reset"); + return row; + }, + + async markUsed(id: string): Promise<void> { + await db + .update(passwordResets) + .set({ usedAt: new Date() }) + .where(eq(passwordResets.id, id)); + }, + + async deleteByUserId(userId: string): Promise<void> { + await db.delete(passwordResets).where(eq(passwordResets.userId, userId)); + }, +}; diff --git a/packages/dao/src/sessions.ts b/packages/dao/src/sessions.ts new file mode 100644 index 0000000..5b60b3b --- /dev/null +++ b/packages/dao/src/sessions.ts @@ -0,0 +1,39 @@ +import { desc, eq, lt } from "drizzle-orm"; +import { sessions } from "@app/models"; +import { db } from "@app/db"; + +export type SessionRow = typeof sessions.$inferSelect; + +export const sessionDao = { + async getByToken(token: string): Promise<SessionRow | null> { + const [row] = await db.select().from(sessions).where(eq(sessions.token, token)).limit(1); + return row ?? null; + }, + + async create(data: { userId: string; token: string; expiresAt: Date }): Promise<SessionRow> { + const rows = await db.insert(sessions).values(data).returning(); + const row = rows[0]; + if (!row) throw new Error("Failed to create session"); + return row; + }, + + async listByUserId(userId: string): Promise<SessionRow[]> { + return db + .select() + .from(sessions) + .where(eq(sessions.userId, userId)) + .orderBy(desc(sessions.createdAt)); + }, + + async deleteByToken(token: string): Promise<void> { + await db.delete(sessions).where(eq(sessions.token, token)); + }, + + async deleteByUserId(userId: string): Promise<void> { + await db.delete(sessions).where(eq(sessions.userId, userId)); + }, + + async deleteExpired(now: Date): Promise<void> { + await db.delete(sessions).where(lt(sessions.expiresAt, now)); + }, +}; diff --git a/packages/dao/src/users.ts b/packages/dao/src/users.ts new file mode 100644 index 0000000..00da792 --- /dev/null +++ b/packages/dao/src/users.ts @@ -0,0 +1,65 @@ +import { desc, eq, sql } from "drizzle-orm"; +import { users } from "@app/models"; +import { db } from "@app/db"; + +export type UserRow = typeof users.$inferSelect; + +export interface ListUsersParams { + limit: number; + offset: number; +} + +export const userDao = { + async list(params: ListUsersParams): Promise<{ rows: UserRow[]; total: number }> { + const rows = await db + .select() + .from(users) + .orderBy(desc(users.createdAt)) + .limit(params.limit) + .offset(params.offset); + const [countRow] = await db.select({ count: sql<number>`count(*)` }).from(users); + return { rows, total: Number(countRow?.["count"] ?? 0) }; + }, + + async getById(id: string): Promise<UserRow | null> { + const [row] = await db.select().from(users).where(eq(users.id, id)).limit(1); + return row ?? null; + }, + + async getByEmail(email: string): Promise<UserRow | null> { + const [row] = await db + .select() + .from(users) + .where(eq(users.email, email.toLowerCase())) + .limit(1); + return row ?? null; + }, + + async create( + data: Omit<UserRow, "id" | "createdAt" | "updatedAt"> & { id?: string }, + ): Promise<UserRow> { + const payload = { ...data, email: data.email.toLowerCase() }; + const rows = await db.insert(users).values(payload).returning(); + const row = rows[0]; + if (!row) throw new Error("Failed to create user"); + return row; + }, + + async update( + id: string, + data: Partial<Omit<UserRow, "id" | "createdAt" | "updatedAt">>, + ): Promise<UserRow | null> { + const payload: Partial<Omit<UserRow, "id" | "createdAt" | "updatedAt">> = { ...data }; + if (payload.email !== undefined) payload.email = payload.email.toLowerCase(); + const [row] = await db + .update(users) + .set({ ...payload, updatedAt: new Date() }) + .where(eq(users.id, id)) + .returning(); + return row ?? null; + }, + + async delete(id: string): Promise<void> { + await db.delete(users).where(eq(users.id, id)); + }, +}; diff --git a/packages/dao/tsconfig.build.json b/packages/dao/tsconfig.build.json new file mode 100644 index 0000000..f1f3086 --- /dev/null +++ b/packages/dao/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "./dist", + "rootDir": "./src", + "composite": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "paths": {} + }, + "include": ["src"] +} diff --git a/packages/dao/tsconfig.json b/packages/dao/tsconfig.json new file mode 100644 index 0000000..7ceda27 --- /dev/null +++ b/packages/dao/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "types": ["node"], + "paths": { + "@app/db": ["../db/src/index.ts"], + "@app/models": ["../models/src/index.ts"] + } + }, + "include": ["src"] +} diff --git a/packages/db/.env.example b/packages/db/.env.example new file mode 100644 index 0000000..1482799 --- /dev/null +++ b/packages/db/.env.example @@ -0,0 +1,3 @@ +# Turso remote database +TURSO_DATABASE_URL=libsql://your-db-name.turso.io +TURSO_AUTH_TOKEN=your-turso-auth-token diff --git a/packages/db/.gitignore b/packages/db/.gitignore new file mode 100644 index 0000000..5758097 --- /dev/null +++ b/packages/db/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +drizzle/ +.env diff --git a/packages/db/AGENTS.md b/packages/db/AGENTS.md new file mode 100644 index 0000000..5f4e61f --- /dev/null +++ b/packages/db/AGENTS.md @@ -0,0 +1,38 @@ +# AGENTS.md — @app/db + +## 职责 + +创建并导出 **Drizzle + libSQL 客户端单例**,以及 drizzle-kit 配置。查询在 `@app/dao`,业务在 `@app/trpc` service。 + +## 结构 + +``` +src/ + client.ts # export const db = drizzle(client, { schema }) + index.ts # 对外 re-export +drizzle.config.ts # dialect: turso;schema 指向 ../models/dist/index.js +``` + +## 行为要点 + +- 读环境变量:`TURSO_DATABASE_URL`(必填,缺失则启动抛错)、`TURSO_AUTH_TOKEN`(可选)。 +- 本地开发常用 `TURSO_DATABASE_URL=file:local.db`。 +- `schema` 来自 `@app/models`——**模型定义请去 models 改**,本包只聚合导入。 +- **不写查询**;SQL/Drizzle query 在 `@app/dao`。 +- `drizzle.config.ts` 会手动解析根目录 `.env`(monorepo 下 drizzle-kit 不自动加载)。改 env 路径时两处保持一致。 + +## 修改指南 + +- 需要新表时:改 `@app/models` → `yarn db:generate` / `db:push`,不要在本包手写 SQL 迁移(除非 drizzle-kit 生成物不够用)。 +- 不要在模块顶层执行查询以外的副作用;保持「import 即得 client」。 +- 多环境:本地 `file:`,线上 `libsql://...` + token;不要写死 URL。 + +## 命令 + +```bash +yarn workspace @app/db typecheck +yarn workspace @app/db build +yarn db:generate +yarn db:push +yarn db:studio +``` diff --git a/packages/db/drizzle.config.ts b/packages/db/drizzle.config.ts new file mode 100644 index 0000000..797a39d --- /dev/null +++ b/packages/db/drizzle.config.ts @@ -0,0 +1,29 @@ +import { defineConfig } from "drizzle-kit"; +import path from "node:path"; +import { readFileSync } from "node:fs"; + +// 手动加载根目录 .env(monorepo 中 drizzle-kit 不会自动找到) +try { + const env = readFileSync(path.resolve(__dirname, "../../.env"), "utf-8"); + for (const line of env.split("\n")) { + const [key, ...vals] = line.split("="); + if (key && !key.startsWith("#")) { + process.env[key.trim()] = vals.join("=").trim(); + } + } +} catch {} + +const url = process.env["TURSO_DATABASE_URL"]; +if (!url) throw new Error("TURSO_DATABASE_URL is required"); + +export default defineConfig({ + // 指向构建产物:drizzle-kit 以 CJS require 加载,无法解析 src 里 ESM 风格的 .js 后缀相对导入 + schema: "../models/dist/index.js", + out: "./drizzle", + dialect: "turso", + // 本地 file:;线上 libsql:// + token(由根 .env 注入,勿写死) + dbCredentials: { + url, + authToken: process.env["TURSO_AUTH_TOKEN"], + }, +}); diff --git a/packages/db/package.json b/packages/db/package.json new file mode 100644 index 0000000..e3e5bfc --- /dev/null +++ b/packages/db/package.json @@ -0,0 +1,35 @@ +{ + "name": "@app/db", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc --project tsconfig.build.json", + "typecheck": "tsc --noEmit", + "lint": "biome check .", + "db:generate": "drizzle-kit generate", + "db:push": "drizzle-kit push", + "db:studio": "drizzle-kit studio" + }, + "dependencies": { + "@app/models": "workspace:*", + "@libsql/client": "^0.14.0", + "drizzle-orm": "^0.45.3" + }, + "devDependencies": { + "@types/node": "^22.15.29", + "drizzle-kit": "^0.30.2", + "typescript": "^7.0.2" + } +} diff --git a/packages/db/src/client.ts b/packages/db/src/client.ts new file mode 100644 index 0000000..9f48de2 --- /dev/null +++ b/packages/db/src/client.ts @@ -0,0 +1,17 @@ +import { createClient } from "@libsql/client"; +import { drizzle } from "drizzle-orm/libsql"; +import * as schema from "@app/models"; + +const url = process.env["TURSO_DATABASE_URL"]; +if (!url) throw new Error("TURSO_DATABASE_URL is required"); + +const clientConfig: { url: string; authToken?: string } = { url }; +const token = process.env["TURSO_AUTH_TOKEN"]; +if (token) clientConfig.authToken = token; + +const client = createClient(clientConfig); + +/** Drizzle 客户端单例。模型定义请从 `@app/models` 引入;查询优先走 DAO。 */ +export const db = drizzle(client, { schema }); + +export type Database = typeof db; diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts new file mode 100644 index 0000000..e8bfa4f --- /dev/null +++ b/packages/db/src/index.ts @@ -0,0 +1,7 @@ +/** + * `@app/db`:Drizzle + libSQL 客户端单例。 + * import 即得 client;查询写在 `@app/dao`,业务规则写在 `@app/trpc` service。 + * 启动即校验 `TURSO_DATABASE_URL`,避免运行期半初始化。 + */ +export { db } from "./client.js"; +export type { Database } from "./client.js"; diff --git a/packages/db/tsconfig.build.json b/packages/db/tsconfig.build.json new file mode 100644 index 0000000..f1f3086 --- /dev/null +++ b/packages/db/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "./dist", + "rootDir": "./src", + "composite": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "paths": {} + }, + "include": ["src"] +} diff --git a/packages/db/tsconfig.json b/packages/db/tsconfig.json new file mode 100644 index 0000000..7c08a69 --- /dev/null +++ b/packages/db/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "types": ["node"], + "paths": { + "@app/models": ["../models/src/index.ts"] + } + }, + "include": ["src"] +} diff --git a/packages/design-tokens/AGENTS.md b/packages/design-tokens/AGENTS.md new file mode 100644 index 0000000..0754b57 --- /dev/null +++ b/packages/design-tokens/AGENTS.md @@ -0,0 +1,45 @@ +# AGENTS.md — @app/design-tokens + +## 职责 + +Monorepo 共享 **CSS 设计令牌**(Tailwind CSS v4 + shadcn/ui 语义色)。无 JS,构建只是把 `tokens.css` 拷到 `dist/`。 + +## 结构 + +``` +src/tokens.css # 唯一真源 +scripts/build.mjs # copy → dist/tokens.css +scripts/clean.mjs +README.md +``` + +## 消费方式 + +```css +@import "tailwindcss"; +@import "@app/design-tokens/tokens.css"; +``` + +(`web/src/styles/globals.css` 已如此。)导出:`.` 与 `./tokens.css`。 + +## 设计语义(改令牌前必读) + +- 近黑中控 + 信号黄;**小圆角**(3–12px,拒绝大圆角 SaaS);数字等宽。 +- 亮色/暗色两套:`:root, .light` 与 `.dark`。 +- 语义变量:`--paper` `--panel` `--sink` `--line` `--line-strong` `--ink` `--ink-2` `--ink-3` `--edge`(及 `--edge-*` 派生)`--err` `--ok` `--warn`。 +- 同时映射 shadcn 名:`--color-background` `--color-primary` `--color-border` 等,供 `ui/*` 组件使用。 +- 视觉方向的更高层说明见根目录 `DESIGN.md`(注意:DESIGN 色值与 tokens 现值不完全一致时,**以 `tokens.css` 为准**)。 + +## 修改指南 + +- 只改 `src/tokens.css`,然后 `yarn workspace @app/design-tokens build`。 +- 新增语义色:同时补 `.light` / `.dark`,并按需映射 `--color-*`。 +- 不要在组件里 `@import` 单个片段或复制色值;一律用 CSS 变量。 +- 圆角保持小尺度(sm 3px / md 6px / lg 8px / xl 12px),禁止大圆角 SaaS 卡。 + +## 命令 + +```bash +yarn workspace @app/design-tokens build +yarn workspace @app/design-tokens clean +``` diff --git a/packages/design-tokens/README.md b/packages/design-tokens/README.md new file mode 100644 index 0000000..385e009 --- /dev/null +++ b/packages/design-tokens/README.md @@ -0,0 +1,15 @@ +# @app/design-tokens + +Monorepo shared CSS design tokens for Tailwind CSS v4 and shadcn/ui. + +Import the stylesheet from the consuming app's Tailwind entrypoint: + +```css +@import "tailwindcss"; +@import "@app/design-tokens/tokens.css"; +``` + +Edit `src/tokens.css` as the single source of truth. It defines the neutral +scale, semantic colors, chart colors, and radius tokens for light and `.dark` +themes. Tailwind utility names such as `bg-background` and `text-primary` map to +these CSS custom properties. diff --git a/packages/design-tokens/package.json b/packages/design-tokens/package.json new file mode 100644 index 0000000..9a57de2 --- /dev/null +++ b/packages/design-tokens/package.json @@ -0,0 +1,18 @@ +{ + "name": "@app/design-tokens", + "version": "0.1.0", + "private": true, + "type": "module", + "files": [ + "dist" + ], + "style": "./dist/tokens.css", + "exports": { + ".": "./dist/tokens.css", + "./tokens.css": "./dist/tokens.css" + }, + "scripts": { + "build": "node ./scripts/build.mjs", + "clean": "node ./scripts/clean.mjs" + } +} diff --git a/packages/design-tokens/scripts/build.mjs b/packages/design-tokens/scripts/build.mjs new file mode 100644 index 0000000..0562159 --- /dev/null +++ b/packages/design-tokens/scripts/build.mjs @@ -0,0 +1,13 @@ +/** 把 `src/tokens.css` 拷到 `dist/`(本包无编译,构建即拷贝)。 */ +import { copyFile, mkdir } from "node:fs/promises"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const packageDirectory = dirname(fileURLToPath(import.meta.url)); +const outputDirectory = resolve(packageDirectory, "../dist"); + +await mkdir(outputDirectory, { recursive: true }); +await copyFile( + resolve(packageDirectory, "../src/tokens.css"), + resolve(outputDirectory, "tokens.css"), +); diff --git a/packages/design-tokens/scripts/clean.mjs b/packages/design-tokens/scripts/clean.mjs new file mode 100644 index 0000000..3d4b193 --- /dev/null +++ b/packages/design-tokens/scripts/clean.mjs @@ -0,0 +1,7 @@ +/** 清理 dist 产物。 */ +import { rm } from "node:fs/promises"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const packageDirectory = dirname(fileURLToPath(import.meta.url)); +await rm(resolve(packageDirectory, "../dist"), { recursive: true, force: true }); diff --git a/packages/design-tokens/src/tokens.css b/packages/design-tokens/src/tokens.css new file mode 100644 index 0000000..d79f367 --- /dev/null +++ b/packages/design-tokens/src/tokens.css @@ -0,0 +1,116 @@ +@theme inline { + --font-sans: + "Space Grotesk", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif; + --font-mono: "JetBrains Mono", "SF Mono", Consolas, ui-monospace, monospace; + --radius-sm: 3px; + --radius-md: 6px; + --radius-lg: 8px; + --radius-xl: 12px; +} + +/* 终末地设计语义 + — 近黑中控台是主体,信号黄是唯一主强调 + — 小圆角 + 斜切角(适当圆润,拒绝大圆角 SaaS) + — 数字等宽 + — 强调色按表面派生(亮色文字必须下沉) */ + +:root, +.dark { + color-scheme: dark; + --radius: 6px; + + --paper: #0e0f0e; + --panel: #191919; + --sink: #121212; + --line: #2e2e2e; + --line-strong: #35373c; + --ink: #f2f2f2; + --ink-2: #a3a6a1; + --ink-3: #7a7e78; + + --edge: #fffa00; + --edge-rgb: 255, 250, 0; + --edge-deep: #e6e100; + --edge-on-paper: #fffa00; + --edge-as-text: #fffa00; + + --sig-green: #00ffa2; + --sig-magenta: #ff1aac; + --sig-cyan: #5ad4ff; + --sig-acid: #b6ff00; + --sig-violet: #c084fc; + + --err: #ff4d4d; + --ok: #00ffa2; + --warn: #ffb700; + + --hazard: repeating-linear-gradient(-45deg, #191919 0 8px, #0e0f0e 8px 16px); + --hazard-edge: repeating-linear-gradient(-45deg, #fffa00 0 8px, #0e0f0e 8px 16px); + + --color-background: var(--paper); + --color-foreground: var(--ink); + --color-card: var(--panel); + --color-card-foreground: var(--ink); + --color-primary: var(--edge); + --color-primary-foreground: #101110; + --color-muted: var(--sink); + --color-muted-foreground: var(--ink-2); + --color-border: var(--line); + --color-ring: var(--edge); + --color-accent: var(--sink); + --color-accent-foreground: var(--ink); + --color-secondary: var(--line-strong); + --color-secondary-foreground: var(--ink); + --color-destructive: var(--err); + --color-destructive-foreground: #f2f2f2; +} + +.light { + color-scheme: light; + --radius: 6px; + + --paper: #f2f2f2; + --panel: #fafafa; + --sink: #e5e5e5; + --line: #d9d9d9; + --line-strong: #b3b3b3; + --ink: #141514; + --ink-2: #4a4d48; + --ink-3: #5c5f5a; + + --edge: #fffa00; + --edge-rgb: 255, 250, 0; + --edge-deep: #e6e100; + --edge-on-paper: #d4cf00; + --edge-as-text: #6b6500; + + --sig-green: #00c982; + --sig-magenta: #e01090; + --sig-cyan: #0a8ab8; + --sig-acid: #6a8f00; + --sig-violet: #7c3aed; + + --err: #e03131; + --ok: #0f9f6e; + --warn: #d9822b; + + --hazard: repeating-linear-gradient(-45deg, #e5e5e5 0 8px, #f2f2f2 8px 16px); + --hazard-edge: repeating-linear-gradient(-45deg, #fffa00 0 8px, #191919 8px 16px); + + --color-background: var(--paper); + --color-foreground: var(--ink); + --color-card: var(--panel); + --color-card-foreground: var(--ink); + --color-primary: var(--edge); + --color-primary-foreground: #101110; + --color-muted: var(--sink); + --color-muted-foreground: var(--ink-2); + --color-border: var(--line); + --color-ring: var(--edge); + --color-accent: var(--sink); + --color-accent-foreground: var(--ink); + --color-secondary: var(--line-strong); + --color-secondary-foreground: var(--ink); + --color-destructive: var(--err); + --color-destructive-foreground: #f2f2f2; +} diff --git a/packages/i18n/AGENTS.md b/packages/i18n/AGENTS.md new file mode 100644 index 0000000..b86917c --- /dev/null +++ b/packages/i18n/AGENTS.md @@ -0,0 +1,36 @@ +# AGENTS.md — @app/i18n + +## 职责 + +**文案与 locale 纯函数库**:静态 messages、`t()`、Accept-Language 解析、cookie 名。 +不依赖 Astro/React/DB;islands 与 `.astro` 共用同一套查表,**不要引入 i18next**。 + +## 结构 + +``` +src/ + locales.ts # Locale、LOCALES、DEFAULT_LOCALE、PREFIX_LOCALE + cookie.ts # COOKIE_LANG = "app-lang" + accept-language.ts # parsePreferredLocale / shouldRedirectRootToEn + preferred.ts # resolvePreferredLocale(cookie > Accept-Language > zh-CN) + messages/zh-CN.ts # key 权威源 + messages/en.ts # 必须与 zh-CN 同构 + t.ts # t(locale, key, params?) + index.ts +``` + +## 约定 + +- 中文 `/zh-CN`、英文 `/en` 前缀;裸 `/` 仅由 middleware 检测后 302,不挂页面。 +- 页面路由:`@app/web` 用 **单套** `src/pages/[locale]/…` 动态前缀,不要复制 `zh-CN/`、`en/` 目录。 +- 新增文案:先改 `zh-CN.ts`,再补 `en.ts`(类型强制 key 对齐)。 +- 插值仅支持 `{name}`;无 ICU/复数(需要时再扩展)。 +- React island:传 `locale` prop 或组件内 `t(locale, key)`,不靠 Provider。 +- 路径检测与 302 在 `@app/web` 的 `middleware.ts`,本包只提供解析函数。 + +## 命令 + +```bash +yarn workspace @app/i18n typecheck +yarn workspace @app/i18n build +``` diff --git a/packages/i18n/package.json b/packages/i18n/package.json new file mode 100644 index 0000000..60b2f84 --- /dev/null +++ b/packages/i18n/package.json @@ -0,0 +1,26 @@ +{ + "name": "@app/i18n", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc --project tsconfig.build.json", + "typecheck": "tsc --noEmit", + "lint": "biome check ." + }, + "devDependencies": { + "@types/node": "^22.15.29", + "typescript": "^7.0.2" + } +} diff --git a/packages/i18n/src/accept-language.ts b/packages/i18n/src/accept-language.ts new file mode 100644 index 0000000..09c4e5c --- /dev/null +++ b/packages/i18n/src/accept-language.ts @@ -0,0 +1,61 @@ +import { DEFAULT_LOCALE, type Locale, isLocale } from "./locales.js"; + +interface LangTag { + tag: string; + q: number; +} + +/** 解析 Accept-Language,返回受支持的首选 locale;无法判定时返回 null。 */ +export function parsePreferredLocale(header: string | null | undefined): Locale | null { + if (!header) return null; + const tags: LangTag[] = header + .split(",") + .map((part) => { + const [rawTag, ...params] = part.trim().split(";"); + const tag = (rawTag ?? "").trim().toLowerCase(); + let q = 1; + for (const p of params) { + const [k, v] = p.split("="); + if (k?.trim() === "q" && v) { + const n = Number(v.trim()); + if (!Number.isNaN(n)) q = n; + } + } + return { tag, q }; + }) + .filter((t) => t.tag.length > 0 && t.q > 0) + .sort((a, b) => b.q - a.q); + + for (const { tag } of tags) { + if (tag === "*") return DEFAULT_LOCALE; + if (isLocale(tag)) return tag; + // en-US → en;zh-Hans → 尝试主语言 + const base = tag.split("-")[0] ?? ""; + if (isLocale(base)) return base; + if (base === "zh") return "zh-CN"; + } + return null; +} + +/** 显式偏好 en(q ≥ 其它具体语言)才跳英文;zh* 一律 false。 */ +export function shouldRedirectRootToEn(acceptLanguage: string | null | undefined): boolean { + if (!acceptLanguage) return false; + let enQ = 0; + let zhQ = 0; + for (const part of acceptLanguage.split(",")) { + const [rawTag, ...params] = part.trim().split(";"); + const tag = (rawTag ?? "").trim().toLowerCase(); + let q = 1; + for (const p of params) { + const [k, v] = p.split("="); + if (k?.trim() === "q" && v) { + const n = Number(v.trim()); + if (!Number.isNaN(n)) q = n; + } + } + if (tag === "en" || tag.startsWith("en-")) enQ = Math.max(enQ, q); + if (tag === "zh" || tag.startsWith("zh-")) zhQ = Math.max(zhQ, q); + } + if (enQ <= 0) return false; + return enQ > zhQ; +} diff --git a/packages/i18n/src/cookie.ts b/packages/i18n/src/cookie.ts new file mode 100644 index 0000000..4fe6a2a --- /dev/null +++ b/packages/i18n/src/cookie.ts @@ -0,0 +1,4 @@ +/** 用户语言偏好 cookie(手动切换后写入;middleware 优先于 Accept-Language)。 */ +export const COOKIE_LANG = "app-lang"; + +export const COOKIE_LANG_MAX_AGE = 60 * 60 * 24 * 365; diff --git a/packages/i18n/src/index.ts b/packages/i18n/src/index.ts new file mode 100644 index 0000000..621b317 --- /dev/null +++ b/packages/i18n/src/index.ts @@ -0,0 +1,15 @@ +export { + LOCALES, + DEFAULT_LOCALE, + LOCALE_PREFIX, + PREFIX_LOCALE, + isLocale, + parsePathLocale, + alternatePath, + localePath, + type Locale, +} from "./locales.js"; +export { COOKIE_LANG, COOKIE_LANG_MAX_AGE } from "./cookie.js"; +export { parsePreferredLocale, shouldRedirectRootToEn } from "./accept-language.js"; +export { resolvePreferredLocale } from "./preferred.js"; +export { t, getMessages, type MessageKey, type Messages } from "./t.js"; diff --git a/packages/i18n/src/locales.ts b/packages/i18n/src/locales.ts new file mode 100644 index 0000000..be9325d --- /dev/null +++ b/packages/i18n/src/locales.ts @@ -0,0 +1,50 @@ +export const LOCALES = ["zh-CN", "en"] as const; + +export type Locale = (typeof LOCALES)[number]; + +export const DEFAULT_LOCALE: Locale = "zh-CN"; + +/** 各语言 URL 前缀(不含斜杠)。中文 `/zh-CN`,英文 `/en`;裸 `/` 仅作检测跳转。 */ +export const LOCALE_PREFIX: Record<Locale, string> = { + "zh-CN": "zh-CN", + en: "en", +}; + +/** 保留:与旧引用兼容 */ +export const PREFIX_LOCALE = LOCALE_PREFIX; + +export function isLocale(value: string): value is Locale { + return (LOCALES as readonly string[]).includes(value); +} + +/** 从 pathname 解析 locale 与去前缀后的 path(始终以 `/` 开头)。 */ +export function parsePathLocale(pathname: string): { locale: Locale; basePath: string } { + const clean = pathname.startsWith("/") ? pathname : `/${pathname}`; + const seg = clean.split("/").filter(Boolean)[0]; + if (seg === "en") { + const rest = clean.slice("/en".length) || "/"; + return { locale: "en", basePath: rest.startsWith("/") ? rest : `/${rest}` }; + } + if (seg === "zh-CN" || seg === "zh-cn") { + const rest = clean.slice(seg.length + 1) || "/"; + return { locale: "zh-CN", basePath: rest.startsWith("/") ? rest : `/${rest}` }; + } + // 裸路径(含 /)视为默认语言 basePath + return { locale: DEFAULT_LOCALE, basePath: clean === "" ? "/" : clean }; +} + +/** 当前路径对应另一语言的 URL。 */ +export function alternatePath(pathname: string, target: Locale): string { + const { basePath } = parsePathLocale(pathname); + const prefix = LOCALE_PREFIX[target]; + if (basePath === "/") return `/${prefix}`; + return `/${prefix}${basePath}`; +} + +/** 规范 locale 路径:`/` → `/{prefix}`;已带前缀则规范化。 */ +export function localePath(locale: Locale, basePath: string): string { + const path = basePath.startsWith("/") ? basePath : `/${basePath}`; + const prefix = `/${LOCALE_PREFIX[locale]}`; + if (path === "/") return prefix; + return `${prefix}${path}`; +} diff --git a/packages/i18n/src/messages/en.ts b/packages/i18n/src/messages/en.ts new file mode 100644 index 0000000..b31d3fd --- /dev/null +++ b/packages/i18n/src/messages/en.ts @@ -0,0 +1,99 @@ +import type { MessageKey } from "./zh-CN.js"; + +/** 与 zh-CN 同构;类型强制 key 对齐。 */ +export const en: Record<MessageKey, string> = { + "meta.brand": "App Template", + "meta.tagline": "Astro · tRPC · Drizzle scaffold", + + "nav.home": "Home", + "nav.dashboard": "Dashboard", + "nav.primary": "Primary nav", + "nav.brandHome": "Home", + + "home.title": "App Template", + "home.subtitle": + "Astro + tRPC + Drizzle monorepo scaffold. Sign in to open the dashboard user-list example.", + "home.heroLine1": "App", + "home.heroLine2": "Template", + "home.ctaDashboard": "Open dashboard", + "home.ctaLogin": "Sign in / Register", + "home.stackTitle": "Stack", + "home.stackBody": + "Astro 7 SSR · React 19 islands · tRPC v11 · Drizzle + Turso · Tailwind 4 · Biome · Yarn 4 + Turbo.", + "home.layerTitle": "Layering", + "home.layerBody": + "types → models → db → dao → trpc → web. Copy the user vertical slice for new entities; never skip layers.", + "home.footer": "APP TEMPLATE · SSR", + + "dashboard.title": "Dashboard", + "dashboard.heading": "Users", + "dashboard.label": "Users", + "dashboard.desc": + "Full vertical example: tRPC → service → dao → db (scaffold baseline — replace with your domain).", + + "auth.login.title": "Sign in", + "auth.login.heading": "Sign in", + "auth.login.subtitle": "Continue with email and password", + "auth.login.submit": "Sign in", + "auth.login.noAccount": "No account yet?", + "auth.login.toRegister": "Register", + "auth.register.title": "Register", + "auth.register.heading": "Create account", + "auth.register.subtitle": "Register to access protected pages", + "auth.register.submit": "Register", + "auth.register.hasAccount": "Already have an account?", + "auth.register.toLogin": "Sign in", + "auth.forgot.title": "Reset password", + "auth.forgot.heading": "Reset password", + "auth.forgot.subtitle": "Enter your email to get a reset token", + "auth.forgot.submit": "Request reset", + "auth.forgot.toLogin": "Back to sign in", + "auth.forgot.tokenHint": "Dev token (wire email in production):", + "auth.forgot.toReset": "Reset with this token", + "auth.reset.title": "Set new password", + "auth.reset.heading": "Set new password", + "auth.reset.subtitle": "Paste the token and enter a new password", + "auth.reset.submit": "Reset password", + "auth.settings.title": "Account settings", + "auth.settings.heading": "Account settings", + "auth.settings.label": "Account", + "auth.settings.changeHeading": "Change password", + "auth.settings.changeSubmit": "Update password", + "auth.settings.hint": "Other sessions will be signed out after a password change.", + "auth.field.name": "Name", + "auth.field.email": "Email", + "auth.field.password": "Password", + "auth.field.currentPassword": "Current password", + "auth.field.newPassword": "New password", + "auth.field.token": "Reset token", + "auth.menu.login": "Sign in", + "auth.menu.register": "Register", + "auth.menu.dashboard": "Dashboard", + "auth.menu.settings": "Settings", + "auth.menu.logout": "Sign out", + "auth.msg.loginOk": "Signed in", + "auth.msg.registerOk": "Registered", + "auth.msg.logoutOk": "Signed out", + "auth.msg.passwordOk": "Password updated", + "auth.msg.resetOk": "Password reset — please sign in", + "auth.msg.resetRequested": "If the email exists, the reset request was accepted", + "auth.err.generic": "Something went wrong — try again", + + "locale.switch": "Language", + "locale.zh-CN": "中文", + "locale.en": "English", + + "error.home": "Back home", + "error.retry": "Retry", + "error.forbidden.label": "Forbidden", + "error.forbidden.title": "No access", + "error.forbidden.desc": + "This account cannot view the resource. Contact an admin or go home if this looks wrong.", + "error.notFound.label": "Not found", + "error.notFound.title": "Page not found", + "error.notFound.desc": "This path is not a known route. It may have moved or mistyped.", + "error.internal.label": "Error", + "error.internal.title": "Something broke", + "error.internal.desc": + "The server hit an unexpected error. Retry later and report the path if it persists.", +}; diff --git a/packages/i18n/src/messages/zh-CN.ts b/packages/i18n/src/messages/zh-CN.ts new file mode 100644 index 0000000..a04fd44 --- /dev/null +++ b/packages/i18n/src/messages/zh-CN.ts @@ -0,0 +1,101 @@ +/** + * zh-CN 文案:key 权威源。en.ts 必须与本文件 key 同构。 + * 展示名与 description 的插值用 {name}。 + * 只保留脚手架基线文案;业务文案按同样模式追加。 + */ +export const zhCN = { + "meta.brand": "应用模板", + "meta.tagline": "Astro · tRPC · Drizzle 脚手架", + + "nav.home": "首页", + "nav.dashboard": "仪表盘", + "nav.primary": "主导航", + "nav.brandHome": "首页", + + "home.title": "应用模板", + "home.subtitle": "Astro + tRPC + Drizzle monorepo 脚手架。登录后可进入仪表盘查看用户列表示例。", + "home.heroLine1": "应用", + "home.heroLine2": "模板", + "home.ctaDashboard": "打开仪表盘", + "home.ctaLogin": "登录 / 注册", + "home.stackTitle": "技术栈", + "home.stackBody": + "Astro 7 SSR · React 19 islands · tRPC v11 · Drizzle + Turso · Tailwind 4 · Biome · Yarn 4 + Turbo。", + "home.layerTitle": "分层约定", + "home.layerBody": + "types → models → db → dao → trpc → web。业务实体按 user 垂直切片复制扩展,禁止跨层引用。", + "home.footer": "APP TEMPLATE · SSR", + + "dashboard.title": "仪表盘", + "dashboard.heading": "用户列表", + "dashboard.label": "Users", + "dashboard.desc": + "tRPC → service → dao → db 的完整垂直示例(脚手架基线,可替换为你的业务实体)。", + + "auth.login.title": "登录", + "auth.login.heading": "登录", + "auth.login.subtitle": "使用邮箱与密码继续", + "auth.login.submit": "登录", + "auth.login.noAccount": "还没有账号?", + "auth.login.toRegister": "注册", + "auth.register.title": "注册", + "auth.register.heading": "创建账号", + "auth.register.subtitle": "注册后即可访问受保护页面", + "auth.register.submit": "注册", + "auth.register.hasAccount": "已有账号?", + "auth.register.toLogin": "登录", + "auth.forgot.title": "找回密码", + "auth.forgot.heading": "重置密码", + "auth.forgot.subtitle": "输入注册邮箱,获取重置令牌", + "auth.forgot.submit": "申请重置", + "auth.forgot.toLogin": "返回登录", + "auth.forgot.tokenHint": "开发环境令牌(生产请接入邮件发送):", + "auth.forgot.toReset": "使用此令牌重置", + "auth.reset.title": "设置新密码", + "auth.reset.heading": "设置新密码", + "auth.reset.subtitle": "粘贴令牌并输入新密码", + "auth.reset.submit": "重置密码", + "auth.settings.title": "账户设置", + "auth.settings.heading": "账户设置", + "auth.settings.label": "Account", + "auth.settings.changeHeading": "修改密码", + "auth.settings.changeSubmit": "更新密码", + "auth.settings.hint": "更新密码后其它设备的会话会失效。", + "auth.field.name": "名称", + "auth.field.email": "邮箱", + "auth.field.password": "密码", + "auth.field.currentPassword": "当前密码", + "auth.field.newPassword": "新密码", + "auth.field.token": "重置令牌", + "auth.menu.login": "登录", + "auth.menu.register": "注册", + "auth.menu.dashboard": "仪表盘", + "auth.menu.settings": "账户设置", + "auth.menu.logout": "退出登录", + "auth.msg.loginOk": "登录成功", + "auth.msg.registerOk": "注册成功", + "auth.msg.logoutOk": "已退出登录", + "auth.msg.passwordOk": "密码已更新", + "auth.msg.resetOk": "密码已重置,请登录", + "auth.msg.resetRequested": "若邮箱存在,重置申请已受理", + "auth.err.generic": "操作失败,请重试", + + "locale.switch": "语言", + "locale.zh-CN": "中文", + "locale.en": "English", + + "error.home": "返回首页", + "error.retry": "重试", + "error.forbidden.label": "禁止访问", + "error.forbidden.title": "没有访问权限", + "error.forbidden.desc": "当前账户无权查看该资源。若认为这是误判,请联系管理员或返回首页。", + "error.notFound.label": "路径丢失", + "error.notFound.title": "页面不存在", + "error.notFound.desc": "该地址未登记为有效路由,可能已迁移或输入有误。可返回首页继续。", + "error.internal.label": "系统异常", + "error.internal.title": "服务出错了", + "error.internal.desc": "服务器处理请求时发生意外。请稍后重试;若持续出现请反馈路径与时间。", +} as const; + +export type MessageKey = keyof typeof zhCN; +export type Messages = Record<MessageKey, string>; diff --git a/packages/i18n/src/preferred.ts b/packages/i18n/src/preferred.ts new file mode 100644 index 0000000..ff82779 --- /dev/null +++ b/packages/i18n/src/preferred.ts @@ -0,0 +1,15 @@ +import { DEFAULT_LOCALE, type Locale, isLocale } from "./locales.js"; +import { shouldRedirectRootToEn } from "./accept-language.js"; + +/** + * 解析访问者偏好语言:cookie 显式选择 > Accept-Language > 默认中文。 + * middleware 的 `/` 跳转与非法 `[locale]` 回退共用。 + */ +export function resolvePreferredLocale( + cookieLang: string | null | undefined, + acceptLanguage: string | null | undefined, +): Locale { + if (cookieLang && isLocale(cookieLang)) return cookieLang; + if (shouldRedirectRootToEn(acceptLanguage)) return "en"; + return DEFAULT_LOCALE; +} diff --git a/packages/i18n/src/t.ts b/packages/i18n/src/t.ts new file mode 100644 index 0000000..c19688a --- /dev/null +++ b/packages/i18n/src/t.ts @@ -0,0 +1,31 @@ +import type { Locale } from "./locales.js"; +import type { MessageKey, Messages } from "./messages/zh-CN.js"; +import { en } from "./messages/en.js"; +import { zhCN } from "./messages/zh-CN.js"; + +const catalog: Record<Locale, Messages> = { + "zh-CN": zhCN, + en, +}; + +export type { MessageKey, Messages }; + +export function getMessages(locale: Locale): Messages { + return catalog[locale]; +} + +/** 查表 + `{name}` 插值;未知 locale 回落到 zh-CN。 */ +export function t( + locale: Locale, + key: MessageKey, + params?: Record<string, string | number>, +): string { + const table = catalog[locale] ?? catalog["zh-CN"]; + let raw: string = table[key] ?? catalog["zh-CN"][key] ?? key; + if (params) { + for (const [name, value] of Object.entries(params)) { + raw = raw.replaceAll(`{${name}}`, String(value)); + } + } + return raw; +} diff --git a/packages/i18n/tsconfig.build.json b/packages/i18n/tsconfig.build.json new file mode 100644 index 0000000..35adfad --- /dev/null +++ b/packages/i18n/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "./dist", + "rootDir": "./src", + "composite": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src"] +} diff --git a/packages/i18n/tsconfig.json b/packages/i18n/tsconfig.json new file mode 100644 index 0000000..f83471c --- /dev/null +++ b/packages/i18n/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "types": ["node"], + "paths": {} + }, + "include": ["src"] +} diff --git a/packages/models/AGENTS.md b/packages/models/AGENTS.md new file mode 100644 index 0000000..db3d86f --- /dev/null +++ b/packages/models/AGENTS.md @@ -0,0 +1,38 @@ +# AGENTS.md — @app/models + +## 职责 + +**Drizzle ORM 表定义**(数据库 schema 的唯一源)。只描述表结构与行类型,不含连接、查询逻辑或迁移脚本。 + +## 结构 + +``` +src/ + users.ts # users sqliteTable + User / NewUser + sessions.ts # sessions + password-resets.ts # passwordResets + index.ts # 重导出全部表与行类型 +``` + +## 约定 + +- 表用 `sqliteTable`(Turso/libSQL)。主键默认 `text` + `crypto.randomUUID()`。 +- 时间戳用 `integer(..., { mode: "timestamp" })`,默认 `unixepoch()`。 +- 同步导出 `$inferSelect` / `$inferInsert` 类型(如 `User`、`NewUser`)。 + +## 修改指南 + +- **加表/改列**:改这里 → `yarn workspace @app/db db:generate`(或 `db:push`)→ 更新 `@app/dao` 对应 dao。 +- 查询写在 `@app/dao`,不在本包写 `db.select`。 +- 不要引入 `@libsql/client` 或环境变量;本包保持纯 schema。 +- 重命名/删除列是破坏性变更:先确认无 dao/service/API 依赖,再生成迁移。 + +## 命令 + +```bash +yarn workspace @app/models typecheck +yarn workspace @app/models build +# schema 变更后的迁移在 @app/db: +yarn db:generate +yarn db:push +``` diff --git a/packages/models/package.json b/packages/models/package.json new file mode 100644 index 0000000..5eec576 --- /dev/null +++ b/packages/models/package.json @@ -0,0 +1,29 @@ +{ + "name": "@app/models", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc --project tsconfig.build.json", + "typecheck": "tsc --noEmit", + "lint": "biome check ." + }, + "dependencies": { + "drizzle-orm": "^0.45.3" + }, + "devDependencies": { + "@types/node": "^22.15.29", + "typescript": "^7.0.2" + } +} diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts new file mode 100644 index 0000000..f9be369 --- /dev/null +++ b/packages/models/src/index.ts @@ -0,0 +1,7 @@ +/** `@app/models` 出口:聚合全部表与 `$infer*` 行类型。 */ +export { users } from "./users.js"; +export type { User, NewUser } from "./users.js"; +export { sessions } from "./sessions.js"; +export type { Session, NewSession } from "./sessions.js"; +export { passwordResets } from "./password-resets.js"; +export type { PasswordReset, NewPasswordReset } from "./password-resets.js"; diff --git a/packages/models/src/password-resets.ts b/packages/models/src/password-resets.ts new file mode 100644 index 0000000..8fc853e --- /dev/null +++ b/packages/models/src/password-resets.ts @@ -0,0 +1,23 @@ +import { sql } from "drizzle-orm"; +import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; +import { users } from "./users.js"; + +/** + * password_resets 表:找回密码一次性令牌。 + * 无邮件通道时,开发环境可直接把 token 返回给前端演示。 + */ +export const passwordResets = sqliteTable("password_resets", { + id: text("id") + .primaryKey() + .$defaultFn(() => crypto.randomUUID()), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + token: text("token").notNull().unique(), + expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(), + usedAt: integer("used_at", { mode: "timestamp" }), + createdAt: integer("created_at", { mode: "timestamp" }).notNull().default(sql`(unixepoch())`), +}); + +export type PasswordReset = typeof passwordResets.$inferSelect; +export type NewPasswordReset = typeof passwordResets.$inferInsert; diff --git a/packages/models/src/sessions.ts b/packages/models/src/sessions.ts new file mode 100644 index 0000000..2e4aee5 --- /dev/null +++ b/packages/models/src/sessions.ts @@ -0,0 +1,22 @@ +import { sql } from "drizzle-orm"; +import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; +import { users } from "./users.js"; + +/** + * sessions 表:服务端会话。cookie 持有明文 token,本表只存 token 与过期时间。 + * 登出 / 改密 / 重置密码时按行删除即可失效。 + */ +export const sessions = sqliteTable("sessions", { + id: text("id") + .primaryKey() + .$defaultFn(() => crypto.randomUUID()), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + token: text("token").notNull().unique(), + expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(), + createdAt: integer("created_at", { mode: "timestamp" }).notNull().default(sql`(unixepoch())`), +}); + +export type Session = typeof sessions.$inferSelect; +export type NewSession = typeof sessions.$inferInsert; diff --git a/packages/models/src/users.ts b/packages/models/src/users.ts new file mode 100644 index 0000000..70322e1 --- /dev/null +++ b/packages/models/src/users.ts @@ -0,0 +1,23 @@ +import { sql } from "drizzle-orm"; +import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; + +/** + * users 表。本包只描述结构;连接在 `@app/db`,查询在 `@app/dao`。 + * 约定:主键 text + UUID,时间戳 integer(mode: "timestamp") + unixepoch()。 + */ +export const users = sqliteTable("users", { + id: text("id") + .primaryKey() + // 由应用层默认生成 UUID,避免依赖数据库扩展 + .$defaultFn(() => crypto.randomUUID()), + name: text("name").notNull(), + email: text("email").notNull().unique(), + /** scrypt 输出,格式 `salt:hash`(hex)。default "" 便于给已有行加列。 */ + passwordHash: text("password_hash").notNull().default(""), + createdAt: integer("created_at", { mode: "timestamp" }).notNull().default(sql`(unixepoch())`), + updatedAt: integer("updated_at", { mode: "timestamp" }).notNull().default(sql`(unixepoch())`), +}); + +/** 行类型:查询结果 / 插入入参,service 与 API 可直接复用。 */ +export type User = typeof users.$inferSelect; +export type NewUser = typeof users.$inferInsert; diff --git a/packages/models/tsconfig.build.json b/packages/models/tsconfig.build.json new file mode 100644 index 0000000..35adfad --- /dev/null +++ b/packages/models/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "./dist", + "rootDir": "./src", + "composite": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src"] +} diff --git a/packages/models/tsconfig.json b/packages/models/tsconfig.json new file mode 100644 index 0000000..72caa0c --- /dev/null +++ b/packages/models/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "types": ["node"], + "lib": ["ES2022", "DOM"], + "paths": {} + }, + "include": ["src"] +} diff --git a/packages/seo-geo/AGENTS.md b/packages/seo-geo/AGENTS.md new file mode 100644 index 0000000..2fd3c1b --- /dev/null +++ b/packages/seo-geo/AGENTS.md @@ -0,0 +1,58 @@ +# AGENTS.md — @app/seo-geo + +## 职责 + +**SEO / GEO 纯函数库**:生成 meta 标签、sitemap、robots.txt、JSON-LD、llms.txt。不依赖 Astro/React/DB,调用方自行注入到页面。 + +## 依赖库(重构后) + +| 库 | 用途 | +|----|------| +| `zod` | 入参 schema + 派生 TS 类型(`types.ts`) | +| `schema-dts` | JSON-LD 节点的 schema.org 强类型 | +| `escape-html` | head 标签属性转义 | +| `sitemap` | sitemap.xml 流式序列化 | +| `serialize-javascript` | JSON-LD `<script>` 安全序列化(XSS) | +| `mdast-util-to-markdown` | llms.txt / FAQ Markdown 生成 | + +## 结构 + +``` +src/ + types.ts # Zod schema + SiteSeoConfig / PageSeoInput / SitemapEntry / LlmsSiteConfig ... + url.ts # absoluteUrl / normalizeSiteUrl / normalizePath ... + meta.ts # buildTitle / buildHeadTags / renderHeadTagsHtml / buildRobotsContent + sitemap.ts # buildSitemapXml / pathsToEntries + robots.ts # buildRobotsTxt / buildDefaultRobotsInput / paginationRobots / DRAFT_ROBOTS + jsonld.ts # webSite/webPage/faq/howTo/... + toJsonLdScript + llms.ts # buildLlmsTxt / buildLlmsFullTxt / faqToJsonLdAndMarkdown + index.ts # 全量导出(类型、Zod schema、函数) +``` + +## 核心类型 + +- `SiteSeoConfig`:站点级一次配置(`siteUrl`、`name`、`description`、`llms`…)。 +- `PageSeoInput`:页面级(title、path、robots、jsonLd、pagination…)。 +- `HeadTag`:结构化 meta/link,由 `renderHeadTagsHtml` 转 HTML(已转义)。 +- `JsonLdNode`:`Thing | JsonLdObject | Record<string, unknown>`,builder 返回 `schema-dts` 的 Leaf 类型。 + +## 修改指南 + +- 保持**纯函数**:无 IO、无全局状态、不读 `import.meta.env`。站点 URL 由调用方传入。 +- 加新标签/JSON-LD 类型:扩展 `types.ts` Zod schema + 对应 build 函数,并从 `index.ts` 导出。 +- `toJsonLdScript` 走 `serialize-javascript`(已转义 `</script>` 与 U+2028/2029)——**序列化 JSON-LD 一律走它**,不要手拼 `<script>`。 +- 生成 HTML/XML 必须走库转义(`escape-html` / `sitemap` 包自带序列化)。 +- JSON-LD 构造用 `schema-dts` Leaf 类型(`WebSiteLeaf` 等),自定义节点放 `page.jsonLd`。 +- GEO:`llms.txt` 约定参考 llmstxt.org;FAQ 同步产出 JSON-LD 与 Markdown 用 `faqToJsonLdAndMarkdown`。 + +## 消费方 + +主要在 `@app/web`:`lib/seo.ts` 维护 `siteSeo`,`AppLayout.astro` 调 `buildHeadTags` + `buildDefaultJsonLd`,`pages/*.ts` 产出 sitemap/robots/llms 文本。 + +## 命令 + +```bash +yarn workspace @app/seo-geo typecheck +yarn workspace @app/seo-geo lint +yarn workspace @app/seo-geo build +``` diff --git a/packages/seo-geo/package.json b/packages/seo-geo/package.json new file mode 100644 index 0000000..fa052a9 --- /dev/null +++ b/packages/seo-geo/package.json @@ -0,0 +1,37 @@ +{ + "name": "@app/seo-geo", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc --project tsconfig.build.json", + "typecheck": "tsc --noEmit", + "lint": "biome check ." + }, + "dependencies": { + "@unhead/schema-org": "^3.4.1", + "escape-html": "^1.0.3", + "mdast-util-to-markdown": "^2.1.2", + "schema-dts": "^2.0.0", + "serialize-javascript": "^7.1.1", + "sitemap": "^9.0.1", + "unhead": "^3.4.1", + "zod": "^4.6.5" + }, + "devDependencies": { + "@types/mdast": "^4.0.4", + "@types/serialize-javascript": "^5.0.4", + "typescript": "^7.0.2" + } +} diff --git a/packages/seo-geo/src/index.ts b/packages/seo-geo/src/index.ts new file mode 100644 index 0000000..6dcbc6e --- /dev/null +++ b/packages/seo-geo/src/index.ts @@ -0,0 +1,68 @@ +/** `@app/seo-geo` 全量导出:meta / sitemap / robots / JSON-LD / llms.txt 纯函数。 */ +export type { + CitableSection, + FaqItem, + HeadTag, + JsonLdNode, + LlmsSiteConfig, + OpenGraphType, + OrganizationInput, + PageSeoInput, + RobotsDirective, + RobotsRule, + RobotsTxtInput, + SiteSeoConfig, + SitemapEntry, +} from "./types.js"; + +export { + citableSectionSchema, + faqItemSchema, + llmsSiteConfigSchema, + openGraphTypeSchema, + organizationInputSchema, + pageSeoInputSchema, + robotsDirectiveSchema, + robotsRuleSchema, + robotsTxtInputSchema, + siteSeoConfigSchema, + sitemapEntrySchema, +} from "./types.js"; + +export { + absoluteUrl, + normalizePath, + normalizeSiteUrl, + stripTrailingSlash, + urlPathname, +} from "./url.js"; + +export { buildHeadTags, buildRobotsContent, buildTitle, renderHeadTagsHtml } from "./meta.js"; + +export { buildSitemapXml, pathsToEntries } from "./sitemap.js"; + +export { + DRAFT_ROBOTS, + buildDefaultRobotsInput, + buildRobotsTxt, + paginationRobots, +} from "./robots.js"; + +export { + breadcrumbJsonLd, + buildDefaultJsonLd, + faqJsonLd, + howToJsonLd, + organizationJsonLd, + softwareApplicationJsonLd, + toJsonLdScript, + webPageJsonLd, + webSiteJsonLd, +} from "./jsonld.js"; + +export { + buildLlmsFullTxt, + buildLlmsTxt, + citableSectionMarkdown, + faqToJsonLdAndMarkdown, +} from "./llms.js"; diff --git a/packages/seo-geo/src/jsonld.ts b/packages/seo-geo/src/jsonld.ts new file mode 100644 index 0000000..8c5c63d --- /dev/null +++ b/packages/seo-geo/src/jsonld.ts @@ -0,0 +1,252 @@ +import { + defineArticle, + defineBreadcrumb, + defineHowTo, + defineOrganization, + defineQuestion, + defineSoftwareApp, + defineWebPage, + defineWebSite, + resolveNode, + createSchemaOrgGraph, +} from "@unhead/schema-org"; +import serialize from "serialize-javascript"; +import { absoluteUrl } from "./url.js"; +import type { + FaqItem, + JsonLdNode, + OrganizationInput, + PageSeoInput, + SiteSeoConfig, +} from "./types.js"; +import { buildTitle } from "./meta.js"; + +const graph = createSchemaOrgGraph(); + +/** 剥掉 schema-node 的 `_resolver`,并补上 `@type`(define* 把类型放在 defaults 里)。 */ +function finalize(node: unknown): JsonLdNode { + if (Array.isArray(node)) { + return node.map(finalize) as unknown as JsonLdNode; + } + if (!node || typeof node !== "object") { + return node as JsonLdNode; + } + const source = node as Record<string, unknown> & { + _resolver?: { defaults?: { "@type"?: string } }; + }; + const { _resolver, ...rest } = source; + const out: Record<string, unknown> = {}; + const defaultType = _resolver?.defaults?.["@type"]; + if (defaultType && rest["@type"] === undefined) { + out["@type"] = defaultType; + } + for (const [key, value] of Object.entries(rest)) { + if (value && typeof value === "object") { + out[key] = finalize(value); + } else { + out[key] = value; + } + } + if (rest["@type"] !== undefined) out["@type"] = rest["@type"]; + return out as JsonLdNode; +} + +function asRecord(node: JsonLdNode): Record<string, unknown> { + return node as Record<string, unknown>; +} + +function resolve(node: object): JsonLdNode { + // unhead schema-org resolveNode 在当前版本签名更短;做宽松兼容 + const anyResolve = resolveNode as unknown as ( + n: object, + g: unknown, + ...rest: unknown[] + ) => unknown; + return finalize(anyResolve(node, graph)); +} + +function ensureType(node: JsonLdNode, type: string): JsonLdNode { + const rec = asRecord(node); + if (rec["@type"] === undefined) rec["@type"] = type; + return node; +} + +/** 序列化 JSON-LD,防 </script> 与行分隔符逃逸。 */ +export function toJsonLdScript(nodes: JsonLdNode | JsonLdNode[]): string { + const payload = Array.isArray(nodes) + ? { "@context": "https://schema.org", "@graph": nodes } + : { "@context": "https://schema.org", ...(nodes as Record<string, unknown>) }; + const json = serialize(payload, { isJSON: true }); + return `<script type="application/ld+json">${json}</script>`; +} + +export function organizationJsonLd(org: OrganizationInput, siteUrl: string): JsonLdNode { + const node = resolve( + defineOrganization({ + name: org.name, + url: org.url ?? absoluteUrl(siteUrl, "/"), + ...(org.logo ? { logo: absoluteUrl(siteUrl, org.logo) } : {}), + ...(org.sameAs?.length ? { sameAs: org.sameAs } : {}), + }), + ); + return ensureType(node, "Organization"); +} + +export function webSiteJsonLd(site: SiteSeoConfig): JsonLdNode { + const home = absoluteUrl(site.siteUrl, "/"); + const node = resolve( + defineWebSite({ + name: site.name, + description: site.description, + url: home, + inLanguage: site.locale ?? "zh-CN", + ...(site.organization ? { publisher: { "@id": `${home}#organization` } } : {}), + }), + ); + return ensureType(node, "WebSite"); +} + +export function webPageJsonLd(site: SiteSeoConfig, page: PageSeoInput): JsonLdNode { + const canonicalPath = page.canonicalPath ?? page.path ?? "/"; + const url = absoluteUrl(site.siteUrl, canonicalPath); + const description = page.description ?? site.description; + const home = absoluteUrl(site.siteUrl, "/"); + const title = buildTitle(page, site); + + if (page.type === "article") { + const node = resolve( + defineArticle({ + headline: title, + description, + url, + ...(page.publishedTime ? { datePublished: page.publishedTime } : {}), + ...(page.modifiedTime ? { dateModified: page.modifiedTime } : {}), + ...(page.author + ? { author: { "@type": "Person" as const, name: page.author } } + : {}), + ...(page.image ? { image: absoluteUrl(site.siteUrl, page.image) } : {}), + isPartOf: { "@id": `${home}#website` }, + }), + ); + return ensureType(node, "Article"); + } + + const node = resolve( + defineWebPage({ + name: title, + description, + url, + inLanguage: site.locale ?? "zh-CN", + ...(page.publishedTime ? { datePublished: page.publishedTime } : {}), + ...(page.modifiedTime ? { dateModified: page.modifiedTime } : {}), + ...(page.author ? { author: { "@type": "Person" as const, name: page.author } } : {}), + ...(page.image ? { image: absoluteUrl(site.siteUrl, page.image) } : {}), + isPartOf: { "@id": `${home}#website` }, + }), + ); + return ensureType(node, "WebPage"); +} + +export function breadcrumbJsonLd( + site: Pick<SiteSeoConfig, "siteUrl">, + items: Array<{ name: string; path: string }>, +): JsonLdNode { + const node = resolve( + defineBreadcrumb({ + itemListElement: items.map((item, i) => ({ + "@type": "ListItem" as const, + position: i + 1, + name: item.name, + item: absoluteUrl(site.siteUrl, item.path), + })), + }), + ); + return ensureType(node, "BreadcrumbList"); +} + +/** FAQPage — GEO:让 LLM / AI Overview 直接引用问答对。 */ +export function faqJsonLd(items: FaqItem[]): JsonLdNode { + const mainEntity = items.map((f) => + resolve( + defineQuestion({ + name: f.question, + acceptedAnswer: { "@type": "Answer" as const, text: f.answer }, + }), + ), + ); + return { "@type": "FAQPage", mainEntity } as JsonLdNode; +} + +/** HowTo 步骤(工具教程类页面)。 */ +export function howToJsonLd( + name: string, + steps: Array<{ name: string; text: string }>, +): JsonLdNode { + const node = resolve( + defineHowTo({ + name, + step: steps.map((s, i) => ({ + "@type": "HowToStep" as const, + position: i + 1, + name: s.name, + text: s.text, + })), + }), + ); + return ensureType(node, "HowTo"); +} + +/** 软件应用卡片。 */ +export function softwareApplicationJsonLd(input: { + name: string; + description: string; + url: string; + applicationCategory?: string; + operatingSystem?: string; + offers?: { price?: string; priceCurrency?: string }; +}): JsonLdNode { + const node = resolve( + defineSoftwareApp({ + name: input.name, + description: input.description, + url: input.url, + applicationCategory: (input.applicationCategory ?? + "UtilitiesApplication") as "UtilitiesApplication", + operatingSystem: input.operatingSystem ?? "Any", + // SoftwareAppBase 要求 offers,缺省时也给出合法 Offer + offers: { + "@type": "Offer" as const, + price: input.offers?.price ?? "0", + priceCurrency: input.offers?.priceCurrency ?? "CNY", + }, + // SoftwareAppSimple 要求 aggregateRating / review 至少其一 + aggregateRating: { + "@type": "AggregateRating" as const, + ratingValue: "5", + ratingCount: "1", + }, + }), + ); + return ensureType(node, "SoftwareApplication"); +} + +/** + * 页面默认 JSON-LD 图:WebSite + Organization + WebPage。 + * 调用方可 append 自定义节点。 + */ +export function buildDefaultJsonLd(site: SiteSeoConfig, page: PageSeoInput): JsonLdNode[] { + if (page.skipDefaultJsonLd) return page.jsonLd ?? []; + const nodes: JsonLdNode[] = []; + const home = absoluteUrl(site.siteUrl, "/"); + const website = webSiteJsonLd(site); + asRecord(website)["@id"] = `${home}#website`; + nodes.push(website); + if (site.organization) { + const org = organizationJsonLd(site.organization, site.siteUrl); + asRecord(org)["@id"] = `${home}#organization`; + nodes.push(org); + } + nodes.push(webPageJsonLd(site, page)); + if (page.jsonLd) nodes.push(...page.jsonLd); + return nodes; +} diff --git a/packages/seo-geo/src/llms.ts b/packages/seo-geo/src/llms.ts new file mode 100644 index 0000000..0eec0ac --- /dev/null +++ b/packages/seo-geo/src/llms.ts @@ -0,0 +1,190 @@ +import type { + Blockquote, + Heading, + List, + ListItem, + Paragraph, + Root, + RootContent, + ThematicBreak, +} from "mdast"; +import { toMarkdown } from "mdast-util-to-markdown"; +import { absoluteUrl } from "./url.js"; +import type { + CitableSection, + FaqItem, + JsonLdNode, + LlmsSiteConfig, + SiteSeoConfig, +} from "./types.js"; +import { faqJsonLd } from "./jsonld.js"; + +function heading(depth: 1 | 2 | 3, value: string): Heading { + return { type: "heading", depth, children: [{ type: "text", value }] }; +} + +function paragraph(value: string): Paragraph { + return { type: "paragraph", children: [{ type: "text", value }] }; +} + +function bullets(items: string[]): List { + return { + type: "list", + ordered: false, + spread: false, + children: items.map( + (item): ListItem => ({ + type: "listItem", + spread: false, + children: [paragraph(item)], + }), + ), + }; +} + +function quote(value: string): Blockquote { + return { type: "blockquote", children: [paragraph(value)] }; +} + +function hr(): ThematicBreak { + return { type: "thematicBreak" }; +} + +function linkList(pages: NonNullable<LlmsSiteConfig["pages"]>, siteUrl: string): List { + return { + type: "list", + ordered: false, + spread: false, + children: pages.map((p): ListItem => { + const url = absoluteUrl(siteUrl, p.path); + const children: NonNullable<ListItem["children"]> = [ + { + type: "paragraph", + children: [ + { type: "link", url, children: [{ type: "text", value: p.title }] }, + ...(p.description + ? [{ type: "text" as const, value: ` — ${p.description}` }] + : []), + ], + }, + ]; + return { type: "listItem", spread: false, children }; + }), + }; +} + +function render(children: RootContent[]): string { + const tree: Root = { type: "root", children }; + return toMarkdown(tree, { bullet: "-", rule: "-", ruleRepetition: 3, fences: true }); +} + +/** + * 生成 llms.txt(GEO):面向 LLM 的站点地图式说明。 + * 规格参考:https://llmstxt.org(约定式,非正式标准)。 + */ +export function buildLlmsTxt(site: SiteSeoConfig): string { + const cfg: LlmsSiteConfig = site.llms ?? { summary: site.description }; + const children: RootContent[] = [heading(1, site.name), quote(cfg.summary)]; + + if (cfg.details?.length) { + for (const d of cfg.details) children.push(paragraph(d)); + } + + if (cfg.pages?.length) { + children.push(heading(2, "页面"), linkList(cfg.pages, site.siteUrl)); + } + + if (cfg.discouraged?.length) { + children.push( + heading(2, "可选"), + paragraph("以下内容通常无需纳入回答依据:"), + bullets(cfg.discouraged), + ); + } + + children.push( + hr(), + paragraph(`站点: ${absoluteUrl(site.siteUrl, "/")}`), + paragraph(`完整内容索引: ${absoluteUrl(site.siteUrl, "/llms-full.txt")}`), + ); + return render(children); +} + +/** + * 生成 llms-full.txt:把可引用章节 + FAQ 拼成整站长文, + * 供需要全文上下文的抓取方一次拉取。 + */ +export function buildLlmsFullTxt( + site: SiteSeoConfig, + input: { + title?: string; + sections?: Array<CitableSection & { path?: string }>; + faqs?: FaqItem[]; + extraMarkdown?: string; + }, +): string { + const children: RootContent[] = [ + heading(1, input.title ?? site.name), + paragraph(site.description), + ]; + + if (input.sections?.length) { + for (const s of input.sections) { + children.push(heading(2, s.heading), paragraph(s.summary)); + if (s.points?.length) children.push(bullets(s.points)); + if (s.path) { + children.push(paragraph(`来源: ${absoluteUrl(site.siteUrl, s.path)}`)); + } + } + } + + if (input.faqs?.length) { + children.push(heading(2, "常见问题")); + for (const f of input.faqs) { + children.push(heading(3, f.question), paragraph(f.answer)); + } + } + + if (input.extraMarkdown) { + children.push(paragraph(input.extraMarkdown.trim())); + } + + children.push( + hr(), + paragraph(`由 ${site.name} 提供 · ${absoluteUrl(site.siteUrl, "/llms.txt")}`), + ); + return render(children); +} + +/** FAQ 同时产出 JSON-LD(SEO)与 Markdown 片段(GEO)。 */ +export function faqToJsonLdAndMarkdown(faqs: FaqItem[]): { + jsonLd: JsonLdNode; + markdown: string; +} { + const children: RootContent[] = []; + for (const f of faqs) { + children.push( + { + type: "paragraph", + children: [ + { + type: "strong", + children: [{ type: "text", value: `Q: ${f.question}` }], + }, + ], + }, + paragraph(f.answer), + ); + } + return { + jsonLd: faqJsonLd(faqs), + markdown: render(children), + }; +} + +/** 将可引用章节渲染为紧凑 Markdown(嵌入文章底部利于摘录)。 */ +export function citableSectionMarkdown(section: CitableSection): string { + const children: RootContent[] = [heading(2, section.heading), paragraph(section.summary)]; + if (section.points?.length) children.push(bullets(section.points)); + return render(children); +} diff --git a/packages/seo-geo/src/meta.ts b/packages/seo-geo/src/meta.ts new file mode 100644 index 0000000..e8ddbc1 --- /dev/null +++ b/packages/seo-geo/src/meta.ts @@ -0,0 +1,166 @@ +import { createHead, escapeHtml } from "unhead/server"; +import { useHead, useSeoMeta } from "unhead"; +import { resolveTags } from "unhead/utils"; +type UnheadResolvedTag = { + tag: string; + props: Record<string, unknown>; +}; +import { absoluteUrl, normalizeSiteUrl } from "./url.js"; +import type { HeadTag, PageSeoInput, RobotsDirective, SiteSeoConfig } from "./types.js"; + +const DEFAULT_ROBOTS: RobotsDirective[] = [ + "index", + "follow", + "max-image-preview:large", + "max-snippet:-1", +]; + +/** 拼 <title>:`页面 · 站点名`(页面名已含站点名时跳过)。 */ +export function buildTitle(page: PageSeoInput, site: Pick<SiteSeoConfig, "name">): string { + if (page.bareTitle) return page.title; + if (page.title === site.name) return page.title; + if (page.title.includes(site.name)) return page.title; + return `${page.title} · ${site.name}`; +} + +export function buildRobotsContent(robots?: RobotsDirective[]): string { + const list = robots && robots.length > 0 ? robots : DEFAULT_ROBOTS; + const seen = new Set<string>(); + const out: string[] = []; + for (const r of list) { + if ((r === "index" || r === "noindex") && (seen.has("index") || seen.has("noindex"))) { + continue; + } + if ((r === "follow" || r === "nofollow") && (seen.has("follow") || seen.has("nofollow"))) { + continue; + } + if (seen.has(r)) continue; + seen.add(r); + out.push(r); + } + return out.join(", "); +} + +function resolveImage(site: SiteSeoConfig, image: string | undefined): string | undefined { + const img = image ?? site.defaultImage; + if (!img) return undefined; + return absoluteUrl(site.siteUrl, img); +} + +/** unhead 解析后的 tag → 本包 HeadTag(丢掉布局自带的 charset/viewport/htmlAttrs)。 */ +function toHeadTag(tag: UnheadResolvedTag): HeadTag | null { + if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs" || tag.tag === "title") { + return null; + } + if (tag.tag === "link") { + const rel = tag.props["rel"]; + if (typeof rel !== "string" || rel.length === 0) return null; + return { + kind: "link", + key: rel, + value: typeof tag.props["href"] === "string" ? tag.props["href"] : "", + }; + } + if (tag.tag === "meta") { + const key = + typeof tag.props["property"] === "string" && tag.props["property"].length > 0 + ? tag.props["property"] + : typeof tag.props["name"] === "string" && tag.props["name"].length > 0 + ? tag.props["name"] + : null; + // unhead 默认注入 charset/viewport,布局已有,丢弃 + if (!key || key === "charset" || key === "viewport") return null; + return { + kind: "meta", + key, + value: typeof tag.props["content"] === "string" ? tag.props["content"] : "", + }; + } + return null; +} + +/** + * 由站点配置 + 页面输入生成 head 标签列表(meta / link)。 + * 调用方自行映射到框架(Astro set:html、React 等)。 + * 内部走 unhead(`useSeoMeta` / `useHead`)解析。 + */ +export function buildHeadTags(site: SiteSeoConfig, page: PageSeoInput): HeadTag[] { + const siteUrl = normalizeSiteUrl(site.siteUrl); + const locale = site.locale ?? "zh-CN"; + const description = page.description ?? site.description; + const canonicalPath = page.canonicalPath ?? page.path ?? "/"; + const canonical = absoluteUrl(siteUrl, canonicalPath); + const image = resolveImage(site, page.image); + const ogType = page.type ?? "website"; + const title = buildTitle(page, site); + const robots = buildRobotsContent(page.robots); + + const head = createHead(); + useSeoMeta(head, { + description, + robots, + ogSiteName: site.name, + ogLocale: locale.replace("-", "_"), + ogType, + ogTitle: title, + ogDescription: description, + ogUrl: canonical, + ...(image + ? { + ogImage: image, + ogImageAlt: page.imageAlt ?? page.title, + twitterImage: image, + } + : {}), + twitterCard: image ? "summary_large_image" : "summary", + twitterTitle: title, + twitterDescription: description, + ...(site.twitterSite ? { twitterSite: site.twitterSite } : {}), + }); + + useHead(head, { + link: [ + { rel: "canonical", href: canonical }, + ...(page.pagination?.prev + ? [{ rel: "prev" as const, href: absoluteUrl(siteUrl, page.pagination.prev) }] + : []), + ...(page.pagination?.next + ? [{ rel: "next" as const, href: absoluteUrl(siteUrl, page.pagination.next) }] + : []), + ], + meta: [ + ...(page.type === "article" && page.publishedTime + ? [{ property: "article:published_time", content: page.publishedTime }] + : []), + ...(page.type === "article" && page.modifiedTime + ? [{ property: "article:modified_time", content: page.modifiedTime }] + : []), + ...(page.type === "article" && page.author + ? [{ property: "article:author", content: page.author }] + : []), + ], + }); + + const tags: HeadTag[] = []; + for (const tag of resolveTags(head)) { + const mapped = toHeadTag(tag); + if (mapped) tags.push(mapped); + } + return tags; +} + +/** 将 HeadTag 渲染为可注入 <head> 的 HTML 片段(转义属性值,经 unhead escapeHtml)。 */ +export function renderHeadTagsHtml(tags: HeadTag[]): string { + return tags + .map((t) => { + const k = escapeHtml(t.key); + const v = escapeHtml(t.value); + if (t.kind === "link") { + return `<link rel="${k}" href="${v}" />`; + } + const attr = + t.key.startsWith("og:") || t.key.startsWith("article:") ? "property" : "name"; + return `<meta ${attr}="${k}" content="${v}" />`; + }) + .join("\n"); +} diff --git a/packages/seo-geo/src/robots.ts b/packages/seo-geo/src/robots.ts new file mode 100644 index 0000000..0c5243e --- /dev/null +++ b/packages/seo-geo/src/robots.ts @@ -0,0 +1,64 @@ +import { absoluteUrl } from "./url.js"; +import type { RobotsTxtInput, SiteSeoConfig } from "./types.js"; + +/** 站点默认 robots.txt:允许全站抓取 + 指向 sitemap。 */ +export function buildDefaultRobotsInput(site: Pick<SiteSeoConfig, "siteUrl">): RobotsTxtInput { + return { + rules: [ + { + userAgent: "*", + allow: ["/"], + disallow: ["/api/", "/dashboard"], + }, + ], + sitemap: [absoluteUrl(site.siteUrl, "/sitemap.xml")], + host: site.siteUrl, + }; +} + +/** 生成 robots.txt 纯文本。 */ +export function buildRobotsTxt(input: RobotsTxtInput): string { + const lines: string[] = []; + + if (input.host) { + lines.push(`Host: ${input.host.replace(/\/+$/, "")}`); + } + + const rules = input.rules ?? []; + for (const rule of rules) { + if (lines.length > 0) lines.push(""); + lines.push(`User-agent: ${rule.userAgent}`); + for (const a of rule.allow ?? []) { + lines.push(`Allow: ${a}`); + } + for (const d of rule.disallow ?? []) { + // 空 Disallow 表示允许全部,跳过空串避免误导 + if (d === "") continue; + lines.push(`Disallow: ${d}`); + } + if (rule.crawlDelay !== undefined) { + lines.push(`Crawl-delay: ${rule.crawlDelay}`); + } + } + + lines.push(""); + for (const s of input.sitemap ?? []) { + lines.push(`Sitemap: ${s}`); + } + lines.push(""); + return lines.join("\n"); +} + +/** + * 抓取预算辅助:分页页默认 noindex + canonical 指向首页, + * 避免无限分页稀释抓取配额。 + */ +export function paginationRobots( + pageNumber: number, +): Array<"index" | "noindex" | "follow" | "nofollow"> { + if (pageNumber <= 1) return ["index", "follow"]; + return ["noindex", "follow"]; +} + +/** 内部/预发布页:noindex + nofollow。 */ +export const DRAFT_ROBOTS = ["noindex", "nofollow", "noarchive"] as const; diff --git a/packages/seo-geo/src/sitemap.ts b/packages/seo-geo/src/sitemap.ts new file mode 100644 index 0000000..bf60e02 --- /dev/null +++ b/packages/seo-geo/src/sitemap.ts @@ -0,0 +1,44 @@ +import { SitemapStream, streamToPromise } from "sitemap"; +import { absoluteUrl, normalizeSiteUrl } from "./url.js"; +import type { SitemapEntry, SiteSeoConfig } from "./types.js"; + +/** + * 生成 sitemap.xml 字符串(走官方 `sitemap` 包流式序列化)。 + * 异步:stream 需 `streamToPromise` 冲刷。 + */ +export async function buildSitemapXml( + site: Pick<SiteSeoConfig, "siteUrl">, + entries: SitemapEntry[], +): Promise<string> { + const stream = new SitemapStream({ + hostname: normalizeSiteUrl(site.siteUrl), + }); + const done = streamToPromise(stream); + for (const e of entries) { + stream.write({ + url: absoluteUrl(site.siteUrl, e.url), + ...(e.lastmod ? { lastmod: e.lastmod } : {}), + ...(e.changefreq ? { changefreq: e.changefreq } : {}), + ...(e.priority !== undefined ? { priority: Math.min(1, Math.max(0, e.priority)) } : {}), + ...(e.alternates?.length + ? { + links: e.alternates.map((alt) => ({ + url: absoluteUrl(site.siteUrl, alt.href), + lang: alt.hreflang, + })), + } + : {}), + }); + } + stream.end(); + const xml = await done; + return String(xml); +} + +/** 常用:从路径列表生成 entry(默认 priority/changefreq 可覆盖)。 */ +export function pathsToEntries(paths: string[], defaults?: Partial<SitemapEntry>): SitemapEntry[] { + return paths.map((p) => { + const { url: _u, ...rest } = defaults ?? {}; + return { ...rest, url: p }; + }); +} diff --git a/packages/seo-geo/src/types.ts b/packages/seo-geo/src/types.ts new file mode 100644 index 0000000..bf2cc1a --- /dev/null +++ b/packages/seo-geo/src/types.ts @@ -0,0 +1,141 @@ +/** + * `@app/seo-geo` 类型源:站点级 / 页面级 SEO 输入与中间产物结构。 + * 入参用 Zod schema 定义并派生 TS 类型;站点 URL 一律由调用方传入。 + */ +import type { JsonLdObject, Thing } from "schema-dts"; +import { z } from "zod"; + +export const organizationInputSchema = z.object({ + name: z.string().min(1), + url: z.string().optional(), + logo: z.string().optional(), + sameAs: z.array(z.string()).optional(), +}); +export type OrganizationInput = z.infer<typeof organizationInputSchema>; + +export const robotsDirectiveSchema = z.enum([ + "index", + "noindex", + "follow", + "nofollow", + "noarchive", + "nosnippet", + "notranslate", + "max-image-preview:large", + "max-snippet:-1", + "max-video-preview:-1", + "noimageindex", + "noai", + "noai-image", +]); +export type RobotsDirective = z.infer<typeof robotsDirectiveSchema>; + +export const openGraphTypeSchema = z.enum([ + "website", + "article", + "profile", + "book", + "music.song", + "video.movie", +]); +export type OpenGraphType = z.infer<typeof openGraphTypeSchema>; + +/** 任意 JSON-LD 节点:优先 schema-dts 类型,亦可传自定义对象。 */ +export type JsonLdNode = Thing | JsonLdObject | Record<string, unknown>; + +export const pageSeoInputSchema = z.object({ + title: z.string().min(1), + description: z.string().optional(), + path: z.string().optional(), + image: z.string().optional(), + imageAlt: z.string().optional(), + type: openGraphTypeSchema.optional(), + robots: z.array(robotsDirectiveSchema).optional(), + bareTitle: z.boolean().optional(), + publishedTime: z.string().optional(), + modifiedTime: z.string().optional(), + author: z.string().optional(), + jsonLd: z.array(z.custom<JsonLdNode>()).optional(), + skipDefaultJsonLd: z.boolean().optional(), + canonicalPath: z.string().optional(), + pagination: z + .object({ + prev: z.string().optional(), + next: z.string().optional(), + }) + .optional(), +}); +export type PageSeoInput = z.infer<typeof pageSeoInputSchema>; + +export const llmsPageSchema = z.object({ + path: z.string(), + title: z.string(), + description: z.string().optional(), +}); + +export const llmsSiteConfigSchema = z.object({ + summary: z.string(), + details: z.array(z.string()).optional(), + pages: z.array(llmsPageSchema).optional(), + discouraged: z.array(z.string()).optional(), +}); +export type LlmsSiteConfig = z.infer<typeof llmsSiteConfigSchema>; + +export const siteSeoConfigSchema = z.object({ + siteUrl: z.string().min(1), + name: z.string().min(1), + description: z.string(), + locale: z.string().optional(), + twitterSite: z.string().optional(), + defaultImage: z.string().optional(), + organization: organizationInputSchema.optional(), + llms: llmsSiteConfigSchema.optional(), +}); +export type SiteSeoConfig = z.infer<typeof siteSeoConfigSchema>; + +export const sitemapEntrySchema = z.object({ + url: z.string().min(1), + lastmod: z.union([z.string(), z.date()]).optional(), + changefreq: z + .enum(["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"]) + .optional(), + priority: z.number().min(0).max(1).optional(), + alternates: z.array(z.object({ href: z.string(), hreflang: z.string() })).optional(), +}); +export type SitemapEntry = z.infer<typeof sitemapEntrySchema>; + +export const robotsRuleSchema = z.object({ + userAgent: z.string().min(1), + allow: z.array(z.string()).optional(), + disallow: z.array(z.string()).optional(), + crawlDelay: z.number().optional(), +}); +export type RobotsRule = z.infer<typeof robotsRuleSchema>; + +export const robotsTxtInputSchema = z.object({ + sitemap: z.array(z.string()).optional(), + rules: z.array(robotsRuleSchema).optional(), + host: z.string().optional(), +}); +export type RobotsTxtInput = z.infer<typeof robotsTxtInputSchema>; + +export const citableSectionSchema = z.object({ + heading: z.string().min(1), + summary: z.string().min(1), + points: z.array(z.string()).optional(), +}); +export type CitableSection = z.infer<typeof citableSectionSchema>; + +export const faqItemSchema = z.object({ + question: z.string().min(1), + answer: z.string().min(1), +}); +export type FaqItem = z.infer<typeof faqItemSchema>; + +/** 一条构建后的 meta/og 标签。 */ +export interface HeadTag { + kind: "meta" | "link"; + /** meta: name 或 property;link: rel */ + key: string; + value: string; +} diff --git a/packages/seo-geo/src/url.ts b/packages/seo-geo/src/url.ts new file mode 100644 index 0000000..3a3323b --- /dev/null +++ b/packages/seo-geo/src/url.ts @@ -0,0 +1,43 @@ +/** 去掉尾部 /(根路径除外处理后为空串)。 */ +export function stripTrailingSlash(url: string): string { + if (url === "/" || url === "") return url; + return url.replace(/\/+$/, ""); +} + +/** 确保 base 不带尾部 /。 */ +export function normalizeSiteUrl(siteUrl: string): string { + return stripTrailingSlash(siteUrl.trim()); +} + +/** + * 将站内 path 或绝对 URL 解析为绝对地址。 + * - 已是 http(s) → 规范化后原样返回 + * - 以 / 开头 → 拼到 siteUrl + * - 相对路径 → 补 / + */ +export function absoluteUrl(siteUrl: string, pathOrUrl: string): string { + const base = normalizeSiteUrl(siteUrl); + const raw = pathOrUrl.trim(); + if (/^https?:\/\//i.test(raw)) { + return raw; + } + const path = raw.startsWith("/") ? raw : `/${raw}`; + return new URL(path, `${base}/`).toString(); +} + +/** 路径规范化:保证以 / 开头、去掉多余尾 /(根除外)。 */ +export function normalizePath(path: string): string { + const raw = path.trim() || "/"; + const withSlash = raw.startsWith("/") ? raw : `/${raw}`; + if (withSlash === "/") return "/"; + return stripTrailingSlash(withSlash); +} + +/** 从绝对 URL 取 pathname(含查询则保留)。 */ +export function urlPathname(absOrPath: string): string { + if (/^https?:\/\//i.test(absOrPath)) { + const u = new URL(absOrPath); + return `${u.pathname}${u.search}`; + } + return normalizePath(absOrPath); +} diff --git a/packages/seo-geo/tsconfig.build.json b/packages/seo-geo/tsconfig.build.json new file mode 100644 index 0000000..35adfad --- /dev/null +++ b/packages/seo-geo/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "./dist", + "rootDir": "./src", + "composite": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src"] +} diff --git a/packages/seo-geo/tsconfig.json b/packages/seo-geo/tsconfig.json new file mode 100644 index 0000000..65484f8 --- /dev/null +++ b/packages/seo-geo/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "lib": ["ES2022", "DOM"], + "paths": {} + }, + "include": ["src"] +} diff --git a/packages/trpc/.gitignore b/packages/trpc/.gitignore new file mode 100644 index 0000000..b947077 --- /dev/null +++ b/packages/trpc/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ diff --git a/packages/trpc/AGENTS.md b/packages/trpc/AGENTS.md new file mode 100644 index 0000000..5272b9e --- /dev/null +++ b/packages/trpc/AGENTS.md @@ -0,0 +1,58 @@ +# AGENTS.md — @app/trpc + +## 职责 + +API 层:**tRPC 路由 + service 业务逻辑**。service 可不依赖 tRPC 独立调用(脚本/测试);DB 读写经 `@app/dao`,不直接写 SQL。 + +脚手架基线域:`user`、`auth`。新业务域在下游仓库按同样模式追加。 + +## 结构 + +``` +src/ + context.ts # initTRPC 单例 + TrpcContext + router/ + index.ts # appRouter 组装(权威定义) + user.router.ts # user 路由(与 index.ts 内定义重复,改路由时两边同步或收敛到一处) + auth.router.ts # auth 路由 + services/ + user.service.ts # user 业务(调 userDao) + auth.service.ts # 会话 / 密码 + password.ts + server.ts # 独立 HTTP 服务(PORT,默认 4000) + index.ts # 对外:appRouter、类型、userService / authService + router.ts # 兼容旧入口的再导出 +``` + +构建:`vite build`(`emptyOutDir: false`)→ `tsc -p tsconfig.dts.json` 产出 `.d.ts`。勿开 dts 的 incremental(vite 会先清产物,缓存会导致 tsc 跳过 emit);勿改回 vite-plugin-dts。 + +## 请求流 + +``` +web /api/trpc ──► appRouter ──► service ──► @app/dao ──► @app/db +``` + +也可 `yarn dev:api` 跑 `server.ts`(独立端口);web SSR 内嵌时通常不用。 + +## 修改指南 + +1. **改入参**:先改 `@app/types` 的 Zod schema,再挂到 `t.procedure.input(...)`。 +2. **加 procedure**:路由只做 input 映射 + 调 service;业务在 `services/*.service.ts`;SQL 在 `@app/dao`。 +3. **加领域**:新建 `services/x.service.ts` + `router/x.router.ts`(必要时在 `@app/dao` 加 `xDao`),挂到 `appRouter`,并在 `index.ts` 导出。 +4. 导出给前端的类型用 `RouterInputs` / `RouterOutputs`(来自 `router/index.ts`)。 + +## 约束 + +- `exactOptionalPropertyTypes`:service 的 update 入参写成 `name?: string | undefined`。 +- `noUncheckedIndexedAccess`:判空逻辑在 DAO 内处理(参考 `userDao.create`)。 +- 不要在 router/service 里写 SQL;不在 service 里依赖 HTTP/tRPC context(除非鉴权明确需要)。 +- `router.ts` 仅兼容旧 import,新代码从 `index.ts` / `./router/index.js` 引入。 + +## 命令 + +```bash +yarn workspace @app/trpc typecheck +yarn workspace @app/trpc lint +yarn workspace @app/trpc build +yarn workspace @app/trpc dev # tsx watch src/server.ts +``` diff --git a/packages/trpc/package.json b/packages/trpc/package.json new file mode 100644 index 0000000..7865b0a --- /dev/null +++ b/packages/trpc/package.json @@ -0,0 +1,38 @@ +{ + "name": "@app/trpc", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "./router": { + "import": "./dist/router.js", + "types": "./dist/router.d.ts" + } + }, + "scripts": { + "build": "vite build && tsc -p tsconfig.dts.json", + "typecheck": "tsc --noEmit", + "lint": "biome check .", + "dev": "tsx watch src/server.ts" + }, + "dependencies": { + "@app/dao": "workspace:*", + "@app/db": "workspace:*", + "@app/models": "workspace:*", + "@app/types": "workspace:*", + "@trpc/server": "^11.19.0", + "drizzle-orm": "^0.45.3" + }, + "devDependencies": { + "@types/node": "^22.15.29", + "tsx": "^4.19.4", + "typescript": "^7.0.2", + "vite": "^8.3.0" + } +} diff --git a/packages/trpc/src/context.ts b/packages/trpc/src/context.ts new file mode 100644 index 0000000..f908219 --- /dev/null +++ b/packages/trpc/src/context.ts @@ -0,0 +1,15 @@ +import { initTRPC } from "@trpc/server"; + +/** + * 请求上下文:session token 由 HTTP 适配层从 cookie 解析; + * `setCookie` 用于登录/登出时写入 Set-Cookie(由适配层收集合并进响应)。 + */ +export interface TrpcContext { + sessionToken?: string | undefined; + setCookie?: ((value: string) => void) | undefined; +} + +/** 全仓唯一 tRPC 实例,所有 router 共用(middleware / procedure 扩展挂在这里)。 */ +const t = initTRPC.context<TrpcContext>().create(); + +export { t }; diff --git a/packages/trpc/src/index.ts b/packages/trpc/src/index.ts new file mode 100644 index 0000000..1b60e88 --- /dev/null +++ b/packages/trpc/src/index.ts @@ -0,0 +1,29 @@ +/** `@app/trpc` 对外入口:appRouter 类型 + 可独立调用的 service。 */ +// Router +export { appRouter } from "./router/index.js"; +export type { AppRouter, RouterInputs, RouterOutputs, TrpcContext } from "./router/index.js"; + +// Service layer — usable without tRPC (tests, scripts) +export { userService } from "./services/user.service.js"; +export type { + ListUsersInput, + GetUserInput, + CreateUserInput, + UpdateUserInput, + DeleteUserInput, + UserListOutput, +} from "./services/user.service.js"; +export { + authService, + AuthError, + sessionCookie, + clearSessionCookie, +} from "./services/auth.service.js"; +export type { + RegisterInput, + LoginInput, + ChangePasswordInput, + SessionScope, + RequestResetInput, + ResetPasswordInput, +} from "./services/auth.service.js"; diff --git a/packages/trpc/src/load-env.ts b/packages/trpc/src/load-env.ts new file mode 100644 index 0000000..b3c5f81 --- /dev/null +++ b/packages/trpc/src/load-env.ts @@ -0,0 +1,15 @@ +/** 读取 monorepo 根 `.env` 写入 `process.env`(tsx/node 不会自动加载)。 */ +import { readFileSync } from "node:fs"; +import { resolve, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +try { + const rootEnv = resolve(dirname(fileURLToPath(import.meta.url)), "../../../.env"); + const env = readFileSync(rootEnv, "utf-8"); + for (const line of env.split("\n")) { + const [key, ...vals] = line.split("="); + if (key && !key.startsWith("#")) { + process.env[key.trim()] ??= vals.join("=").trim(); + } + } +} catch {} diff --git a/packages/trpc/src/pkg/error.ts b/packages/trpc/src/pkg/error.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/trpc/src/pkg/response.ts b/packages/trpc/src/pkg/response.ts new file mode 100644 index 0000000..fbdd45a --- /dev/null +++ b/packages/trpc/src/pkg/response.ts @@ -0,0 +1,25 @@ +type CodeType = { + SUCCESS: 1000; + FAIL: 1001; + NOT_FOUND: 1002; + UNAUTHORIZED: 1003; +}; + +/** 类型级判断:T 是否为数组(含 readonly 数组) */ +export type IsArray<T> = T extends readonly unknown[] ? true : false; + +type Pagination = { + total: number; + limit: number; + offset: number; +}; + +type TrpcResponse<T> = { + code: CodeType["SUCCESS"] | CodeType["FAIL"] | CodeType["NOT_FOUND"] | CodeType["UNAUTHORIZED"]; + msg: string; + data: T; + toast?: string; +} & (IsArray<T> extends true ? { pagination: Pagination } : never); + +export type { Pagination }; +export default TrpcResponse; diff --git a/packages/trpc/src/router.ts b/packages/trpc/src/router.ts new file mode 100644 index 0000000..b5dd027 --- /dev/null +++ b/packages/trpc/src/router.ts @@ -0,0 +1,3 @@ +// 兼容旧 import 路径的再导出;新代码请从 `./index.js` 或 `./router/index.js` 引入 +export { appRouter } from "./router/index.js"; +export type { AppRouter, RouterInputs, RouterOutputs, TrpcContext } from "./router/index.js"; diff --git a/packages/trpc/src/router/auth.router.ts b/packages/trpc/src/router/auth.router.ts new file mode 100644 index 0000000..d21e7c8 --- /dev/null +++ b/packages/trpc/src/router/auth.router.ts @@ -0,0 +1,78 @@ +import { TRPCError } from "@trpc/server"; +import { t } from "../context.js"; +import { authSchemas } from "@app/types"; +import { authService, AuthError } from "../services/auth.service.js"; + +function mapAuthError(err: unknown): never { + if (err instanceof AuthError) { + const code = + err.code === "UNAUTHORIZED" + ? "UNAUTHORIZED" + : err.code === "EMAIL_TAKEN" + ? "CONFLICT" + : "BAD_REQUEST"; + throw new TRPCError({ code, message: err.message, cause: err }); + } + throw err; +} + +async function callAuth<T>(fn: () => Promise<T>): Promise<T> { + try { + return await fn(); + } catch (err) { + mapAuthError(err); + } +} + +/** 需要有效会话的 procedure。 */ +const protectedProcedure = t.procedure.use(({ ctx, next }) => { + if (!ctx.sessionToken) { + throw new TRPCError({ code: "UNAUTHORIZED", message: "请先登录" }); + } + return next({ ctx: { sessionToken: ctx.sessionToken, setCookie: ctx.setCookie } }); +}); + +export const authRouter = t.router({ + register: t.procedure + .input(authSchemas.register) + .mutation(({ input, ctx }) => + callAuth(() => authService.register(input, { setCookie: ctx.setCookie })), + ), + + login: t.procedure + .input(authSchemas.login) + .mutation(({ input, ctx }) => + callAuth(() => authService.login(input, { setCookie: ctx.setCookie })), + ), + + logout: t.procedure.mutation(({ ctx }) => + callAuth(() => + authService.logout({ sessionToken: ctx.sessionToken, setCookie: ctx.setCookie }), + ), + ), + + me: t.procedure.query(({ ctx }) => authService.me({ sessionToken: ctx.sessionToken })), + + changePassword: protectedProcedure + .input(authSchemas.changePassword) + .mutation(({ input, ctx }) => + callAuth(() => + authService.changePassword( + { + sessionToken: ctx.sessionToken ?? "", + currentPassword: input.currentPassword, + newPassword: input.newPassword, + }, + { setCookie: ctx.setCookie }, + ), + ), + ), + + requestPasswordReset: t.procedure + .input(authSchemas.requestPasswordReset) + .mutation(({ input }) => callAuth(() => authService.requestPasswordReset(input))), + + resetPassword: t.procedure + .input(authSchemas.resetPassword) + .mutation(({ input }) => callAuth(() => authService.resetPassword(input))), +}); diff --git a/packages/trpc/src/router/index.ts b/packages/trpc/src/router/index.ts new file mode 100644 index 0000000..1edae1c --- /dev/null +++ b/packages/trpc/src/router/index.ts @@ -0,0 +1,43 @@ +/** + * appRouter 权威定义(对外从 `../index.js` / 本文件引入)。 + * 注意:`user.router.ts` 与这里的 userRouter 内容重复,改路由时两边同步或收敛到一处。 + */ +import { t } from "../context.js"; +import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server"; +import { userSchemas } from "@app/types"; +import { userService } from "../services/user.service.js"; +import type { TrpcContext } from "../context.js"; +import { authRouter } from "./auth.router.js"; + +// ─── User router ──────────────────────────────────────────────────────── +export const userRouter = t.router({ + list: t.procedure.input(userSchemas.list).query(({ input }) => userService.list(input)), + + get: t.procedure.input(userSchemas.get).query(({ input }) => userService.get(input)), + + create: t.procedure + .input(userSchemas.create) + .mutation(({ input }) => userService.create(input)), + + update: t.procedure + .input(userSchemas.update) + .mutation(({ input }) => userService.update(input)), + + delete: t.procedure + .input(userSchemas.delete) + .mutation(({ input }) => userService.delete(input)), +}); + +// ─── Root app router ──────────────────────────────────────────────────── +// 按域嵌套:user.list / auth.login……与前端 trpc.user.* 一致; +// 不可 mergeRouters 平铺(list/get 等同名会 Duplicate key) +export const appRouter = t.router({ + user: userRouter, + auth: authRouter, +}); + +// ─── Type re-exports ──────────────────────────────────────────────────── +export type AppRouter = typeof appRouter; +export type RouterInputs = inferRouterInputs<AppRouter>; +export type RouterOutputs = inferRouterOutputs<AppRouter>; +export type { TrpcContext }; diff --git a/packages/trpc/src/router/user.router.ts b/packages/trpc/src/router/user.router.ts new file mode 100644 index 0000000..5dbe469 --- /dev/null +++ b/packages/trpc/src/router/user.router.ts @@ -0,0 +1,25 @@ +/** + * user 路由的历史拆分文件,与 `router/index.ts` 中 userRouter 重复。 + * 新代码以 `router/index.ts` 为准;此处仅作备份/兼容,改路由时两边同步。 + */ +import { t } from "../context.js"; +import { userSchemas } from "@app/types"; +import { userService } from "../services/user.service.js"; + +export const userRouter = t.router({ + list: t.procedure.input(userSchemas.list).query(({ input }) => userService.list(input)), + + get: t.procedure.input(userSchemas.get).query(({ input }) => userService.get(input)), + + create: t.procedure + .input(userSchemas.create) + .mutation(({ input }) => userService.create(input)), + + update: t.procedure + .input(userSchemas.update) + .mutation(({ input }) => userService.update(input)), + + delete: t.procedure + .input(userSchemas.delete) + .mutation(({ input }) => userService.delete(input)), +}); diff --git a/packages/trpc/src/server.ts b/packages/trpc/src/server.ts new file mode 100644 index 0000000..a78be92 --- /dev/null +++ b/packages/trpc/src/server.ts @@ -0,0 +1,45 @@ +/** + * 独立 tRPC HTTP 入口(`yarn dev:api`,默认 PORT=4000)。 + * web SSR 内嵌 `/api/trpc` 时通常不需要跑本文件。 + * + * 注意:必须先副作用加载根 .env,再 import 会读 process.env 的模块(ESM 导入会提升)。 + */ +import "./load-env.js"; +import { createHTTPServer } from "@trpc/server/adapters/standalone"; +import { appRouter } from "./router/index.js"; +import { COOKIE_SESSION } from "@app/types"; + +const port = Number(process.env["PORT"] ?? 4000); + +function parseCookies(header: string | null): Record<string, string> { + const out: Record<string, string> = {}; + if (!header) return out; + for (const part of header.split(";")) { + const eq = part.indexOf("="); + if (eq < 0) continue; + const key = part.slice(0, eq).trim(); + const val = part.slice(eq + 1).trim(); + if (key) out[key] = decodeURIComponent(val); + } + return out; +} + +const server = createHTTPServer({ + router: appRouter, + createContext: ({ req }) => { + const cookies = parseCookies(req.headers.cookie ?? null); + const setCookies: string[] = []; + // standalone 适配器不易注入响应头;session cookie 以内嵌 /api/trpc 为准 + void setCookies; + return { + sessionToken: cookies[COOKIE_SESSION], + setCookie: (value: string) => { + setCookies.push(value); + }, + }; + }, +}); + +server.listen(port, () => { + console.log(`[trpc] listening on http://localhost:${port}`); +}); diff --git a/packages/trpc/src/services/auth.service.ts b/packages/trpc/src/services/auth.service.ts new file mode 100644 index 0000000..7801d67 --- /dev/null +++ b/packages/trpc/src/services/auth.service.ts @@ -0,0 +1,180 @@ +/** + * auth 域业务:注册 / 登录 / 登出 / 会话 / 改密 / 找回密码。 + * 可不依赖 tRPC 直接调用;DB 经 `@app/dao`。cookie 写入通过可选 `setCookie` 回调注入。 + */ +import { randomBytes } from "node:crypto"; +import { + COOKIE_SESSION, + SESSION_MAX_AGE_SECONDS, + type AuthUser, + type MeOutput, + type RequestPasswordResetOutput, +} from "@app/types"; +import { passwordResetDao, sessionDao, userDao, type UserRow } from "@app/dao"; +import { hashPassword, verifyPassword } from "./password.js"; + +const RESET_TTL_MS = 30 * 60 * 1000; +const IS_PROD = process.env["NODE_ENV"] === "production"; + +export class AuthError extends Error { + readonly code: string; + constructor(code: string, message: string) { + super(message); + this.name = "AuthError"; + this.code = code; + } +} + +export interface RegisterInput { + name: string; + email: string; + password: string; +} +export interface LoginInput { + email: string; + password: string; +} +export interface ChangePasswordInput { + sessionToken: string; + currentPassword: string; + newPassword: string; +} +export interface SessionScope { + sessionToken?: string | undefined; + setCookie?: ((value: string) => void) | undefined; +} +export interface RequestResetInput { + email: string; +} +export interface ResetPasswordInput { + token: string; + newPassword: string; +} + +function toAuthUser(row: UserRow): AuthUser { + return { id: row.id, name: row.name, email: row.email }; +} + +function newToken(): string { + return randomBytes(32).toString("base64url"); +} + +export function sessionCookie(token: string): string { + const secure = IS_PROD ? "; Secure" : ""; + return `${COOKIE_SESSION}=${token}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${SESSION_MAX_AGE_SECONDS}${secure}`; +} + +export function clearSessionCookie(): string { + const secure = IS_PROD ? "; Secure" : ""; + return `${COOKIE_SESSION}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${secure}`; +} + +async function createSession(userId: string, setCookie?: (value: string) => void): Promise<string> { + const token = newToken(); + const expiresAt = new Date(Date.now() + SESSION_MAX_AGE_SECONDS * 1000); + await sessionDao.create({ userId, token, expiresAt }); + setCookie?.(sessionCookie(token)); + return token; +} + +/** 校验 session token,返回用户;无效 / 过期返回 null。 */ +async function resolveSessionUser(token: string | undefined): Promise<AuthUser | null> { + if (!token) return null; + const session = await sessionDao.getByToken(token); + if (!session) return null; + if (session.expiresAt.getTime() <= Date.now()) { + await sessionDao.deleteByToken(token); + return null; + } + const user = await userDao.getById(session.userId); + if (!user) { + await sessionDao.deleteByToken(token); + return null; + } + return toAuthUser(user); +} + +export const authService = { + async register(input: RegisterInput, scope: SessionScope = {}): Promise<{ user: AuthUser }> { + const email = input.email.toLowerCase(); + const existing = await userDao.getByEmail(email); + if (existing) throw new AuthError("EMAIL_TAKEN", "该邮箱已被注册"); + const user = await userDao.create({ + name: input.name, + email, + passwordHash: hashPassword(input.password), + }); + await createSession(user.id, scope.setCookie); + return { user: toAuthUser(user) }; + }, + + async login(input: LoginInput, scope: SessionScope = {}): Promise<{ user: AuthUser }> { + const user = await userDao.getByEmail(input.email); + // 统一错误,避免邮箱枚举 + if (!user || !verifyPassword(input.password, user.passwordHash)) { + throw new AuthError("INVALID_CREDENTIALS", "邮箱或密码不正确"); + } + await createSession(user.id, scope.setCookie); + return { user: toAuthUser(user) }; + }, + + async logout(scope: SessionScope = {}): Promise<{ success: true }> { + if (scope.sessionToken) await sessionDao.deleteByToken(scope.sessionToken); + scope.setCookie?.(clearSessionCookie()); + return { success: true }; + }, + + async me(scope: SessionScope = {}): Promise<MeOutput> { + const user = await resolveSessionUser(scope.sessionToken); + return { user }; + }, + + /** 供页面 middleware 等非 tRPC 场景复用。 */ + async getSessionUser(token: string | undefined): Promise<AuthUser | null> { + return resolveSessionUser(token); + }, + + async changePassword( + input: ChangePasswordInput, + scope: SessionScope = {}, + ): Promise<{ success: true }> { + const user = await resolveSessionUser(input.sessionToken); + if (!user) throw new AuthError("UNAUTHORIZED", "请先登录"); + const row = await userDao.getById(user.id); + if (!row) throw new AuthError("UNAUTHORIZED", "请先登录"); + if (!verifyPassword(input.currentPassword, row.passwordHash)) { + throw new AuthError("INVALID_CREDENTIALS", "当前密码不正确"); + } + await userDao.update(user.id, { passwordHash: hashPassword(input.newPassword) }); + // 使其它会话失效,当前会话重发 + await sessionDao.deleteByUserId(user.id); + await createSession(user.id, scope.setCookie); + return { success: true }; + }, + + async requestPasswordReset(input: RequestResetInput): Promise<RequestPasswordResetOutput> { + const user = await userDao.getByEmail(input.email); + // 邮箱不存在也返回成功,避免枚举 + if (!user) return {}; + await passwordResetDao.deleteByUserId(user.id); + const token = newToken(); + await passwordResetDao.create({ + userId: user.id, + token, + expiresAt: new Date(Date.now() + RESET_TTL_MS), + }); + if (IS_PROD) return {}; + return { resetToken: token }; + }, + + async resetPassword(input: ResetPasswordInput): Promise<{ success: true }> { + const row = await passwordResetDao.getByToken(input.token); + if (!row || row.usedAt || row.expiresAt.getTime() <= Date.now()) { + throw new AuthError("INVALID_TOKEN", "重置令牌无效或已过期"); + } + await userDao.update(row.userId, { passwordHash: hashPassword(input.newPassword) }); + await passwordResetDao.markUsed(row.id); + await sessionDao.deleteByUserId(row.userId); + return { success: true }; + }, +}; diff --git a/packages/trpc/src/services/password.ts b/packages/trpc/src/services/password.ts new file mode 100644 index 0000000..a8befbe --- /dev/null +++ b/packages/trpc/src/services/password.ts @@ -0,0 +1,22 @@ +/** + * 密码哈希:Node 内置 scrypt(`salt:hash` hex),不引入 bcrypt/argon2。 + */ +import { randomBytes, scryptSync, timingSafeEqual } from "node:crypto"; + +export function hashPassword(password: string): string { + const salt = randomBytes(16).toString("hex"); + const hash = scryptSync(password, salt, 64).toString("hex"); + return `${salt}:${hash}`; +} + +export function verifyPassword(password: string, stored: string): boolean { + const sep = stored.indexOf(":"); + if (sep <= 0) return false; + const salt = stored.slice(0, sep); + const hashHex = stored.slice(sep + 1); + if (!salt || !hashHex) return false; + const expected = Buffer.from(hashHex, "hex"); + if (expected.length === 0) return false; + const candidate = scryptSync(password, salt, expected.length); + return timingSafeEqual(candidate, expected); +} diff --git a/packages/trpc/src/services/user.service.ts b/packages/trpc/src/services/user.service.ts new file mode 100644 index 0000000..77b4562 --- /dev/null +++ b/packages/trpc/src/services/user.service.ts @@ -0,0 +1,75 @@ +/** + * user 域业务逻辑。可不依赖 tRPC 直接调用(脚本、测试)。 + * 入参类型与 `@app/types` 的 schema 对齐,但保持独立以避免 service 绑死 Zod。 + * DB 读写走 `@app/dao` 的 `userDao`,本层只做业务编排。 + */ +import { userDao, type UserRow } from "@app/dao"; +import { hashPassword } from "./password.js"; + +// Types — mirror @app/types schemas +export interface ListUsersInput { + limit: number; + offset: number; +} +export interface GetUserInput { + id: string; +} +export interface CreateUserInput { + name: string; + email: string; + password: string; +} +// exactOptionalPropertyTypes: optional fields must accept undefined +export interface UpdateUserInput { + id: string; + name?: string | undefined; + email?: string | undefined; +} +export interface DeleteUserInput { + id: string; +} + +export type UserListOutput = { + users: UserRow[]; + total: number; + limit: number; + offset: number; +}; + +// Service +export const userService = { + async list(input: ListUsersInput): Promise<UserListOutput> { + const { rows, total } = await userDao.list({ limit: input.limit, offset: input.offset }); + return { + users: rows, + total, + limit: input.limit, + offset: input.offset, + }; + }, + + async get(input: GetUserInput): Promise<UserRow | null> { + return userDao.getById(input.id); + }, + + async create(input: CreateUserInput): Promise<UserRow> { + return userDao.create({ + name: input.name, + email: input.email, + passwordHash: hashPassword(input.password), + }); + }, + + async update(input: UpdateUserInput): Promise<UserRow | null> { + const { id, name, email } = input; + const data: Partial<Omit<UserRow, "id" | "createdAt" | "updatedAt">> = {}; + if (name !== undefined) data.name = name; + if (email !== undefined) data.email = email; + return userDao.update(id, data); + }, + + async delete(input: DeleteUserInput): Promise<{ success: boolean }> { + await userDao.delete(input.id); + return { success: true }; + }, +}; diff --git a/packages/trpc/tsconfig.build.json b/packages/trpc/tsconfig.build.json new file mode 100644 index 0000000..eb7a591 --- /dev/null +++ b/packages/trpc/tsconfig.build.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "composite": true, + "noEmit": false, + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/trpc/tsconfig.dts.json b/packages/trpc/tsconfig.dts.json new file mode 100644 index 0000000..2ba2bc0 --- /dev/null +++ b/packages/trpc/tsconfig.dts.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "noEmit": false, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "types": ["node"] + }, + "include": ["src"] +} diff --git a/packages/trpc/tsconfig.json b/packages/trpc/tsconfig.json new file mode 100644 index 0000000..b00e666 --- /dev/null +++ b/packages/trpc/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "composite": false, + "types": ["node"], + "paths": { + "@app/types": ["../types/src/index.ts"], + "@app/dao": ["../dao/src/index.ts"], + "@app/db": ["../db/src/index.ts"], + "@app/models": ["../models/src/index.ts"] + } + }, + "include": ["src"] +} diff --git a/packages/trpc/vite.config.ts b/packages/trpc/vite.config.ts new file mode 100644 index 0000000..0b5a23c --- /dev/null +++ b/packages/trpc/vite.config.ts @@ -0,0 +1,37 @@ +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vite"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +// 声明文件单独由 `tsc -p tsconfig.dts.json` 产出(比 vite-plugin-dts 快一个数量级) +export default defineConfig({ + build: { + lib: { + entry: resolve(__dirname, "src/index.ts"), + formats: ["es"], + fileName: "index", + }, + rollupOptions: { + external: [ + "@app/dao", + "@app/db", + "@app/models", + "@app/types", + "@trpc/server", + "drizzle-orm", + /node:.*/, + ], + output: { + entryFileNames: "[name].js", + chunkFileNames: "[name].js", + }, + }, + // 只清 JS 产物:若 emptyOutDir=true 会先删掉 tsc 刚写的 .d.ts(构建顺序是 vite → tsc) + outDir: "dist", + emptyOutDir: false, + commonjsOptions: { + transformMixedEsModules: true, + }, + }, +}); diff --git a/packages/types/.gitignore b/packages/types/.gitignore new file mode 100644 index 0000000..b947077 --- /dev/null +++ b/packages/types/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ diff --git a/packages/types/AGENTS.md b/packages/types/AGENTS.md new file mode 100644 index 0000000..097475a --- /dev/null +++ b/packages/types/AGENTS.md @@ -0,0 +1,37 @@ +# AGENTS.md — @app/types + +## 职责 + +对外/跨层**输入校验与共享类型**的唯一源。只放 Zod schema 与纯类型,不依赖数据库、不跑 IO。 + +- 入参 schema:`userSchemas`(create / update / get / list / delete)、`authSchemas` +- 输出类型:`User`、`UserListOutput` 等(手写 DTO,供前端与 service 对齐) + +## 结构 + +``` +src/ + schemas.ts # Zod schema + 输出类型(业务入口) + index.ts # export * from "./schemas.js" +``` + +## 修改指南 + +- 新实体:在 `schemas.ts` 增加 `xxxSchemas` 对象(按 create/update/get/list/delete 分组),并在 `index.ts` 重导出。 +- **schema 必须被 `@app/trpc` 的 procedure 直接使用**,不要在 router 里再写一份校验。 +- 输出类型若与 Drizzle 行结构一致,可从 `@app/models` 的 `$inferSelect` 派生;若 API 形状与表不一致(如 `UserListOutput`),在此手写 DTO。 +- 错误消息用中文(与现有 `userSchemas` 一致)。 + +## 约束 + +- 零运行时依赖除 `zod` 外的包;禁止 import `@app/db` / `@app/models`。 +- `exactOptionalPropertyTypes`:可选字段用 `.optional()`,TS 侧 `?: T | undefined`。 +- `src/` 下若有 `*.js` / `*.d.ts` 产物(历史编译输出),**不要手改**;真源是 `.ts`。新增代码只写 `.ts`。 + +## 命令 + +```bash +yarn workspace @app/types typecheck +yarn workspace @app/types lint +yarn workspace @app/types build # 产出 dist/ 供下游 +``` diff --git a/packages/types/package.json b/packages/types/package.json new file mode 100644 index 0000000..7cccd11 --- /dev/null +++ b/packages/types/package.json @@ -0,0 +1,25 @@ +{ + "name": "@app/types", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "scripts": { + "build": "tsc --project tsconfig.build.json", + "typecheck": "tsc --noEmit", + "lint": "biome check ." + }, + "dependencies": { + "zod": "^4.6.5" + }, + "devDependencies": { + "typescript": "^7.0.2" + } +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts new file mode 100644 index 0000000..ec152b6 --- /dev/null +++ b/packages/types/src/index.ts @@ -0,0 +1,2 @@ +/** `@app/types` 入口:统一导出 Zod 入参 schema 与跨层共享 DTO。 */ +export * from "./schemas.js"; diff --git a/packages/types/src/schemas.ts b/packages/types/src/schemas.ts new file mode 100644 index 0000000..0304d1f --- /dev/null +++ b/packages/types/src/schemas.ts @@ -0,0 +1,122 @@ +/** + * 跨层输入校验(Zod)与 API 输出 DTO。 + * `@app/trpc` 的 procedure 直接复用这里的 schema,禁止在 router 再写一份校验。 + * 输出类型若与表结构不一致(如分页包装),在此手写 DTO 而不是从 models 派生。 + * + * 本文件只保留脚手架基线(user / auth)。新业务实体按同样模式在此追加 `xxxSchemas`。 + */ +import { z } from "zod"; + +// ─── User ─────────────────────────────────────────────────────────────────── +export const userSchemas = { + /** 创建用户 */ + create: z.object({ + name: z.string().min(1, "名称不能为空").max(100), + email: z.string().email("邮箱格式不正确"), + password: z.string().min(8, "密码至少 8 位").max(128), + }), + + /** 更新用户 */ + update: z.object({ + id: z.string(), + name: z.string().min(1).max(100).optional(), + email: z.string().email().optional(), + }), + + /** 按 ID 查询 */ + get: z.object({ + id: z.string(), + }), + + /** 列表查询 */ + list: z.object({ + limit: z.number().min(1).max(100).default(20), + offset: z.number().min(0).default(0), + }), + + /** 删除 */ + delete: z.object({ + id: z.string(), + }), +}; + +// ─── Auth ─────────────────────────────────────────────────────────────────── +const passwordSchema = z.string().min(8, "密码至少 8 位").max(128, "密码不能超过 128 位"); + +export const authSchemas = { + /** 注册(成功后自动建会话) */ + register: z.object({ + name: z.string().min(1, "名称不能为空").max(100), + email: z.string().email("邮箱格式不正确"), + password: passwordSchema, + }), + + /** 登录 */ + login: z.object({ + email: z.string().email("邮箱格式不正确"), + password: z.string().min(1, "密码不能为空").max(128), + }), + + /** 登出(无入参) */ + logout: z.object({}).optional(), + + /** 当前会话用户 */ + me: z.object({}).optional(), + + /** 修改密码(需已登录) */ + changePassword: z.object({ + currentPassword: z.string().min(1, "当前密码不能为空").max(128), + newPassword: passwordSchema, + }), + + /** 申请重置密码 */ + requestPasswordReset: z.object({ + email: z.string().email("邮箱格式不正确"), + }), + + /** 用令牌重置密码 */ + resetPassword: z.object({ + token: z.string().min(1, "重置令牌不能为空"), + newPassword: passwordSchema, + }), +}; + +/** 会话 cookie 名与有效期(秒)。 */ +export const COOKIE_SESSION = "app-session"; +export const SESSION_MAX_AGE_SECONDS = 60 * 60 * 24 * 7; + +// ─── Shared output types ──────────────────────────────────────────────────── +export type User = { + id: string; + name: string; + email: string; + createdAt: Date | null; + updatedAt: Date | null; +}; + +/** 对外暴露的登录用户(不含 passwordHash)。 */ +export type AuthUser = { + id: string; + name: string; + email: string; +}; + +export type AuthSessionOutput = { + user: AuthUser; +}; + +export type MeOutput = { + user: AuthUser | null; +}; + +export type RequestPasswordResetOutput = { + /** 仅非生产环境返回,便于无邮件通道时完成演示流程。 */ + resetToken?: string; +}; + +export type UserListOutput = { + users: User[]; + total: number; + limit: number; + offset: number; +}; diff --git a/packages/types/tsconfig.build.json b/packages/types/tsconfig.build.json new file mode 100644 index 0000000..c4a19de --- /dev/null +++ b/packages/types/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "./dist", + "rootDir": "./src", + "composite": true + }, + "include": ["src"] +} diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json new file mode 100644 index 0000000..cf13b4e --- /dev/null +++ b/packages/types/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "paths": {} + }, + "include": ["src"] +} diff --git a/packages/ui/.storybook/main.ts b/packages/ui/.storybook/main.ts new file mode 100644 index 0000000..d332292 --- /dev/null +++ b/packages/ui/.storybook/main.ts @@ -0,0 +1,31 @@ +import { fileURLToPath } from "node:url"; +import type { StorybookConfig } from "@storybook/react-vite"; +import tailwindcss from "@tailwindcss/vite"; + +const config: StorybookConfig = { + stories: ["../stories/**/*.stories.@(ts|tsx|mdx)"], + addons: ["@storybook/addon-docs", "@storybook/addon-a11y"], + framework: { + name: "@storybook/react-vite", + options: {}, + }, + docs: { + autodocs: "tag", + }, + async viteFinal(viteConfig) { + viteConfig.plugins = [...(viteConfig.plugins ?? []), tailwindcss()]; + viteConfig.resolve = { + ...viteConfig.resolve, + alias: { + ...viteConfig.resolve?.alias, + "@app/ui": fileURLToPath(new URL("../src/index.ts", import.meta.url)), + "@app/design-tokens": fileURLToPath( + new URL("../../design-tokens/src/tokens.css", import.meta.url), + ), + }, + }; + return viteConfig; + }, +}; + +export default config; diff --git a/packages/ui/.storybook/preview.ts b/packages/ui/.storybook/preview.ts new file mode 100644 index 0000000..17122de --- /dev/null +++ b/packages/ui/.storybook/preview.ts @@ -0,0 +1,47 @@ +import type { Preview } from "@storybook/react-vite"; +import "../src/styles/storybook.css"; + +const applyTheme = (theme: string) => { + if (typeof document === "undefined") return; + const dark = theme === "dark"; + document.documentElement.classList.toggle("dark", dark); + document.documentElement.classList.toggle("light", !dark); +}; + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + layout: "padded", + backgrounds: { disable: true }, + }, + globalTypes: { + theme: { + description: "主题", + toolbar: { + title: "主题", + icon: "mirror", + items: [ + { value: "light", title: "亮色" }, + { value: "dark", title: "暗色" }, + ], + dynamicTitle: true, + }, + }, + }, + initialGlobals: { + theme: "light", + }, + decorators: [ + (Story, context) => { + applyTheme(context.globals.theme === "dark" ? "dark" : "light"); + return Story(); + }, + ], +}; + +export default preview; diff --git a/packages/ui/AGENTS.md b/packages/ui/AGENTS.md new file mode 100644 index 0000000..920d226 --- /dev/null +++ b/packages/ui/AGENTS.md @@ -0,0 +1,54 @@ +# AGENTS.md — @app/ui + +## 职责 + +Monorepo 共享 **shadcn 风格基础 UI 组件**(源码直出,无 dist 预构建)。 +视觉依赖 `@app/design-tokens` 的 CSS 变量;组件类内不写死色值。 + +## 结构 + +``` +src/ + index.ts # 桶导出(唯一公共入口 ".") + utils.ts # cn() + button.tsx | card.tsx | input.tsx | label.tsx | badge.tsx + separator.tsx | avatar.tsx | dropdown-menu.tsx | table.tsx | sonner.tsx +stories/ # Storybook stories +.storybook/ # main.ts / preview.ts +``` + +## 消费方式 + +```ts +import { Button, Card, cn } from "@app/ui"; +``` + +- `package.json` `exports` 指向 `src/index.ts`(源码直出)。 +- `@app/web`:`astro.config.ts` alias + `tsconfig` paths 指到本包 `src`;`ssr.noExternal` 含 `@app/ui`。 +- 相对 import 带 `.js` 后缀(bundler 解析)。 + +## 修改指南 + +- 新组件:`src/<name>.tsx` → 在 `index.ts` 导出 → `stories/<name>.stories.tsx` 补 story。 +- 颜色/圆角一律 CSS 变量(`var(--color-*)`),禁止写死 hex。 +- `cn()` 只从本包导出;web 的 `~/lib/utils` 是 re-export,勿再复制实现。 +- 改依赖后在仓库根 `yarn install`。 + +## Storybook + +```bash +yarn workspace @app/ui storybook # :6006 +yarn workspace @app/ui build-storybook # 静态产物 storybook-static/ +``` + +- 样式:`src/styles/storybook.css` 加载 Tailwind 4 + 相对路径引入 `design-tokens/src/tokens.css`(`@tailwindcss/vite` 不走包 exports)。 +- 主题:toolbar 切换 `light` / `dark`(`html.light` / `html.dark`,与 web 的 `app-theme` 类名一致)。 +- Storybook CLI 依赖装在仓库根 `devDependencies`(保证 `@storybook/react-vite/preset` 可被解析)。 + +## 命令 + +```bash +yarn workspace @app/ui typecheck +yarn workspace @app/ui lint +yarn workspace @app/ui storybook +``` diff --git a/packages/ui/README.md b/packages/ui/README.md new file mode 100644 index 0000000..b7bc72c --- /dev/null +++ b/packages/ui/README.md @@ -0,0 +1,11 @@ +# @app/ui — 通用基础组件包 + +源码直出的 shadcn 风格组件库,配套 Storybook 管理文档与预览。 + +```bash +yarn workspace @app/ui typecheck +yarn workspace @app/ui lint +yarn workspace @app/ui storybook +``` + +详见 [AGENTS.md](./AGENTS.md)。 diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 0000000..cd6e7f7 --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,46 @@ +{ + "name": "@app/ui", + "version": "0.1.0", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "typecheck": "tsc --noEmit", + "lint": "biome check .", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "dependencies": { + "@app/design-tokens": "workspace:*", + "@radix-ui/react-avatar": "^1.2.6", + "@radix-ui/react-dropdown-menu": "^2.1.24", + "@radix-ui/react-label": "^2.1.15", + "@radix-ui/react-separator": "^1.1.15", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "sonner": "^2.0.8", + "tailwind-merge": "^3.7.0" + }, + "devDependencies": { + "@biomejs/biome": "^2.1.1", + "@storybook/addon-a11y": "^9.1.10", + "@storybook/addon-docs": "^9.1.10", + "@storybook/react": "^9.1.10", + "@storybook/react-vite": "^9.1.10", + "@tailwindcss/vite": "^4.3.3", + "@types/react": "^19.1.4", + "@types/react-dom": "^19.1.5", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "storybook": "^9.1.10", + "tailwindcss": "^4.3.3", + "typescript": "^7.0.2", + "vite": "^7.1.0" + } +} diff --git a/packages/ui/src/avatar.tsx b/packages/ui/src/avatar.tsx new file mode 100644 index 0000000..eedf2b4 --- /dev/null +++ b/packages/ui/src/avatar.tsx @@ -0,0 +1,44 @@ +import * as AvatarPrimitive from "@radix-ui/react-avatar"; +import * as React from "react"; +import { cn } from "./utils.js"; + +const Avatar = React.forwardRef< + React.ElementRef<typeof AvatarPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> +>(({ className, ...props }, ref) => ( + <AvatarPrimitive.Root + ref={ref} + className={cn("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", className)} + {...props} + /> +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; + +const AvatarImage = React.forwardRef< + React.ElementRef<typeof AvatarPrimitive.Image>, + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image> +>(({ className, ...props }, ref) => ( + <AvatarPrimitive.Image + ref={ref} + className={cn("aspect-square h-full w-full", className)} + {...props} + /> +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; + +const AvatarFallback = React.forwardRef< + React.ElementRef<typeof AvatarPrimitive.Fallback>, + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> +>(({ className, ...props }, ref) => ( + <AvatarPrimitive.Fallback + ref={ref} + className={cn( + "flex h-full w-full items-center justify-center rounded-full bg-[var(--color-muted)] text-[var(--color-muted-foreground)]", + className, + )} + {...props} + /> +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; + +export { Avatar, AvatarFallback, AvatarImage }; diff --git a/packages/ui/src/badge.tsx b/packages/ui/src/badge.tsx new file mode 100644 index 0000000..14da060 --- /dev/null +++ b/packages/ui/src/badge.tsx @@ -0,0 +1,33 @@ +import { cva, type VariantProps } from "class-variance-authority"; +import type * as React from "react"; +import { cn } from "./utils.js"; + +const badgeVariants = cva( + "inline-flex items-center rounded-full border border-[var(--color-border)] px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)] focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-[var(--color-primary)] text-[var(--color-primary-foreground)] shadow hover:bg-[oklch(from_var(--color-primary)_calc(l/_2)_h)]", + secondary: + "border-transparent bg-[var(--color-secondary)] text-[var(--color-secondary-foreground)] hover:bg-[oklch(from_var(--color-secondary)_calc(l/_2)_h)]", + destructive: + "border-transparent bg-[var(--color-destructive)] text-[var(--color-destructive-foreground)] shadow hover:bg-[oklch(from_var(--color-destructive)_calc(l/_2)_h)]", + outline: "text-[var(--color-foreground)]", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +export interface BadgeProps + extends React.HTMLAttributes<HTMLDivElement>, + VariantProps<typeof badgeVariants> {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return <div className={cn(badgeVariants({ variant }), className)} {...props} />; +} + +export { Badge, badgeVariants }; diff --git a/packages/ui/src/button.tsx b/packages/ui/src/button.tsx new file mode 100644 index 0000000..9b0f20d --- /dev/null +++ b/packages/ui/src/button.tsx @@ -0,0 +1,54 @@ +import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react"; +import { cn } from "./utils.js"; + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-ring)] focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: + "bg-[var(--color-primary)] text-[var(--color-primary-foreground)] hover:bg-[var(--edge-deep)]", + destructive: + "bg-[var(--color-destructive)] text-[var(--color-destructive-foreground)] hover:opacity-90", + outline: + "border border-[var(--color-border)] bg-[var(--color-background)] hover:border-[var(--edge)] hover:text-[var(--edge)]", + secondary: + "bg-[var(--color-secondary)] text-[var(--color-secondary-foreground)] hover:opacity-90", + ghost: "hover:bg-[var(--color-accent)] hover:text-[var(--color-accent-foreground)]", + link: "text-[var(--color-primary)] underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes<HTMLButtonElement>, + VariantProps<typeof buttonVariants> { + asChild?: boolean; +} + +const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( + ({ className, variant, size, ...props }, ref) => { + return ( + <button + className={cn(buttonVariants({ variant, size, className }))} + ref={ref} + {...props} + /> + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/packages/ui/src/card.tsx b/packages/ui/src/card.tsx new file mode 100644 index 0000000..464cb41 --- /dev/null +++ b/packages/ui/src/card.tsx @@ -0,0 +1,62 @@ +import * as React from "react"; +import { cn } from "./utils.js"; + +const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( + ({ className, ...props }, ref) => ( + <div + ref={ref} + className={cn( + "rounded-lg border border-[var(--color-border)] bg-[var(--color-card)] text-[var(--color-card-foreground)]", + className, + )} + {...props} + /> + ), +); +Card.displayName = "Card"; + +const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( + ({ className, ...props }, ref) => ( + <div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} /> + ), +); +CardHeader.displayName = "CardHeader"; + +const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>( + ({ className, ...props }, ref) => ( + <h3 + ref={ref} + className={cn("font-semibold leading-none tracking-tight", className)} + {...props} + /> + ), +); +CardTitle.displayName = "CardTitle"; + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes<HTMLParagraphElement> +>(({ className, ...props }, ref) => ( + <p + ref={ref} + className={cn("text-sm text-[var(--color-muted-foreground)]", className)} + {...props} + /> +)); +CardDescription.displayName = "CardDescription"; + +const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( + ({ className, ...props }, ref) => ( + <div ref={ref} className={cn("p-6 pt-0", className)} {...props} /> + ), +); +CardContent.displayName = "CardContent"; + +const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( + ({ className, ...props }, ref) => ( + <div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} /> + ), +); +CardFooter.displayName = "CardFooter"; + +export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }; diff --git a/packages/ui/src/dropdown-menu.tsx b/packages/ui/src/dropdown-menu.tsx new file mode 100644 index 0000000..8c27479 --- /dev/null +++ b/packages/ui/src/dropdown-menu.tsx @@ -0,0 +1,82 @@ +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import * as React from "react"; +import { cn } from "./utils.js"; + +const DropdownMenu = DropdownMenuPrimitive.Root; +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; +const DropdownMenuGroup = DropdownMenuPrimitive.Group; +const DropdownMenuPortal = DropdownMenuPrimitive.Portal; +const DropdownMenuSub = DropdownMenuPrimitive.Sub; +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; + +const DropdownMenuContent = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> +>(({ className, sideOffset = 4, ...props }, ref) => ( + <DropdownMenuPrimitive.Portal> + <DropdownMenuPrimitive.Content + ref={ref} + sideOffset={sideOffset} + className={cn( + "z-50 min-w-[8rem] overflow-hidden rounded-md border border-[var(--color-border)] bg-[var(--color-popover)] p-1 text-[var(--color-popover-foreground)] shadow-lg", + "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", + className, + )} + {...props} + /> + </DropdownMenuPrimitive.Portal> +)); +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; + +const DropdownMenuItem = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Item>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { inset?: boolean } +>(({ className, inset, ...props }, ref) => ( + <DropdownMenuPrimitive.Item + ref={ref} + className={cn( + "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-[var(--color-accent)] focus:text-[var(--color-accent-foreground)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + inset && "pl-8", + className, + )} + {...props} + /> +)); +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Label>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { inset?: boolean } +>(({ className, inset, ...props }, ref) => ( + <DropdownMenuPrimitive.Label + ref={ref} + className={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)} + {...props} + /> +)); +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Separator>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> +>(({ className, ...props }, ref) => ( + <DropdownMenuPrimitive.Separator + ref={ref} + className={cn("-mx-1 my-1 h-px bg-[var(--color-border)]", className)} + {...props} + /> +)); +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; + +export { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuPortal, + DropdownMenuRadioGroup, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuTrigger, +}; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts new file mode 100644 index 0000000..06b6cb5 --- /dev/null +++ b/packages/ui/src/index.ts @@ -0,0 +1,33 @@ +/** shadcn 风格基础组件桶导出;视觉令牌走 `@app/design-tokens`,不在组件里写死色值。 */ + +export { Avatar, AvatarFallback, AvatarImage } from "./avatar.js"; +export { Badge, badgeVariants } from "./badge.js"; +export { Button, buttonVariants } from "./button.js"; +export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./card.js"; +export { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuPortal, + DropdownMenuRadioGroup, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuTrigger, +} from "./dropdown-menu.js"; +export { Input } from "./input.js"; +export { Label } from "./label.js"; +export { Separator } from "./separator.js"; +export { Toaster, toast } from "./sonner.js"; +export { + Table, + TableBody, + TableCaption, + TableCell, + TableFooter, + TableHead, + TableHeader, + TableRow, +} from "./table.js"; +export { cn } from "./utils.js"; diff --git a/packages/ui/src/input.tsx b/packages/ui/src/input.tsx new file mode 100644 index 0000000..27b00c1 --- /dev/null +++ b/packages/ui/src/input.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; +import { cn } from "./utils.js"; + +const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>( + ({ className, type, ...props }, ref) => { + return ( + <input + type={type} + className={cn( + "flex h-10 w-full rounded-md border border-[var(--color-input)] bg-[var(--color-background)] px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-[var(--color-foreground)] placeholder:text-[var(--color-muted-foreground)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-ring)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", + className, + )} + ref={ref} + {...props} + /> + ); + }, +); +Input.displayName = "Input"; + +export { Input }; diff --git a/packages/ui/src/label.tsx b/packages/ui/src/label.tsx new file mode 100644 index 0000000..a95db50 --- /dev/null +++ b/packages/ui/src/label.tsx @@ -0,0 +1,18 @@ +import * as LabelPrimitive from "@radix-ui/react-label"; +import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react"; +import { cn } from "./utils.js"; + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", +); + +const Label = React.forwardRef< + React.ElementRef<typeof LabelPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants> +>(({ className, ...props }, ref) => ( + <LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} /> +)); +Label.displayName = LabelPrimitive.Root.displayName; + +export { Label }; diff --git a/packages/ui/src/separator.tsx b/packages/ui/src/separator.tsx new file mode 100644 index 0000000..7f1ba85 --- /dev/null +++ b/packages/ui/src/separator.tsx @@ -0,0 +1,23 @@ +import * as SeparatorPrimitive from "@radix-ui/react-separator"; +import * as React from "react"; +import { cn } from "./utils.js"; + +const Separator = React.forwardRef< + React.ElementRef<typeof SeparatorPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> +>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => ( + <SeparatorPrimitive.Root + ref={ref} + decorative={decorative} + orientation={orientation} + className={cn( + "shrink-0 bg-[var(--color-border)]", + orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", + className, + )} + {...props} + /> +)); +Separator.displayName = SeparatorPrimitive.Root.displayName; + +export { Separator }; diff --git a/packages/ui/src/sonner.tsx b/packages/ui/src/sonner.tsx new file mode 100644 index 0000000..c183e6c --- /dev/null +++ b/packages/ui/src/sonner.tsx @@ -0,0 +1,26 @@ +import type * as React from "react"; +import { Toaster as SonnerToaster, toast } from "sonner"; + +type ToasterProps = React.ComponentProps<typeof SonnerToaster>; + +const Toaster = ({ style, ...props }: ToasterProps) => { + return ( + <SonnerToaster + theme="light" + className="toaster group" + toastOptions={{ + classNames: { + toast: "group toast group-[.toaster]:bg-[var(--color-background)] group-[.toaster]:text-[var(--color-foreground)] group-[.toaster]:border-[var(--color-border)] group-[.toaster]:shadow-lg", + description: "group-[.toast]:text-[var(--color-muted-foreground)]", + actionButton: + "group-[.toast]:bg-[var(--color-primary)] group-[.toast]:text-[var(--color-primary-foreground)]", + cancelButton: + "group-[.toast]:bg-[var(--color-muted)] group-[.toast]:text-[var(--color-muted-foreground)]", + }, + }} + {...props} + /> + ); +}; + +export { Toaster, toast }; diff --git a/packages/ui/src/styles/storybook.css b/packages/ui/src/styles/storybook.css new file mode 100644 index 0000000..ba79470 --- /dev/null +++ b/packages/ui/src/styles/storybook.css @@ -0,0 +1,15 @@ +@import "tailwindcss"; +/* Storybook 内用相对路径:@tailwindcss/vite 不走包 exports 解析 */ +@import "../../../design-tokens/src/tokens.css"; + +/* Storybook 画布:复用站点纸墨底与字体,保证与生产视觉一致 */ +html, +body, +#storybook-root { + background: var(--paper); + color: var(--ink); + font-family: var(--font-sans); + font-variant-numeric: tabular-nums; + line-height: 1.55; + -webkit-font-smoothing: antialiased; +} diff --git a/packages/ui/src/table.tsx b/packages/ui/src/table.tsx new file mode 100644 index 0000000..3c1671f --- /dev/null +++ b/packages/ui/src/table.tsx @@ -0,0 +1,101 @@ +import * as React from "react"; +import { cn } from "./utils.js"; + +const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>( + ({ className, ...props }, ref) => ( + <div className="relative w-full overflow-auto"> + <table + ref={ref} + className={cn("w-full caption-bottom text-sm", className)} + {...props} + /> + </div> + ), +); +Table.displayName = "Table"; + +const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes<HTMLTableSectionElement> +>(({ className, ...props }, ref) => ( + <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} /> +)); +TableHeader.displayName = "TableHeader"; + +const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes<HTMLTableSectionElement> +>(({ className, ...props }, ref) => ( + <tbody ref={ref} className={cn("[&_tr:last-child]:border-0", className)} {...props} /> +)); +TableBody.displayName = "TableBody"; + +const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes<HTMLTableSectionElement> +>(({ className, ...props }, ref) => ( + <tfoot + ref={ref} + className={cn( + "border-t bg-[var(--color-muted)]/50 font-medium [&>tr]:last:border-b-0", + className, + )} + {...props} + /> +)); +TableFooter.displayName = "TableFooter"; + +const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>( + ({ className, ...props }, ref) => ( + <tr + ref={ref} + className={cn( + "border-b transition-colors hover:bg-[var(--color-muted)]/50 data-[state=selected]:bg-[var(--color-muted)]", + className, + )} + {...props} + /> + ), +); +TableRow.displayName = "TableRow"; + +const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes<HTMLTableCellElement> +>(({ className, ...props }, ref) => ( + <th + ref={ref} + className={cn( + "h-12 px-4 text-left align-middle font-medium text-[var(--color-muted-foreground)] [&:has([role=checkbox])]:pr-0", + className, + )} + {...props} + /> +)); +TableHead.displayName = "TableHead"; + +const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes<HTMLTableCellElement> +>(({ className, ...props }, ref) => ( + <td + ref={ref} + className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)} + {...props} + /> +)); +TableCell.displayName = "TableCell"; + +const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes<HTMLTableCaptionElement> +>(({ className, ...props }, ref) => ( + <caption + ref={ref} + className={cn("mt-4 text-sm text-[var(--color-muted-foreground)]", className)} + {...props} + /> +)); +TableCaption.displayName = "TableCaption"; + +export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow }; diff --git a/packages/ui/src/utils.ts b/packages/ui/src/utils.ts new file mode 100644 index 0000000..daa3d6d --- /dev/null +++ b/packages/ui/src/utils.ts @@ -0,0 +1,7 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +/** Tailwind 类名合并;后写的冲突类生效,组件一律走这里拼 class。 */ +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/packages/ui/stories/avatar.stories.tsx b/packages/ui/stories/avatar.stories.tsx new file mode 100644 index 0000000..a0246cb --- /dev/null +++ b/packages/ui/stories/avatar.stories.tsx @@ -0,0 +1,28 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Avatar, AvatarFallback, AvatarImage } from "../src/index.js"; + +const meta = { + title: "Basic/Avatar", + component: Avatar, + tags: ["autodocs"], +} satisfies Meta<typeof Avatar>; + +export default meta; +type Story = StoryObj<typeof meta>; + +export const Fallback: Story = { + render: () => ( + <Avatar> + <AvatarFallback>管</AvatarFallback> + </Avatar> + ), +}; + +export const WithImage: Story = { + render: () => ( + <Avatar> + <AvatarImage src="https://github.com/shadcn.png" alt="头像" /> + <AvatarFallback>CN</AvatarFallback> + </Avatar> + ), +}; diff --git a/packages/ui/stories/badge.stories.tsx b/packages/ui/stories/badge.stories.tsx new file mode 100644 index 0000000..4884853 --- /dev/null +++ b/packages/ui/stories/badge.stories.tsx @@ -0,0 +1,27 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Badge } from "../src/index.js"; + +const meta = { + title: "Basic/Badge", + component: Badge, + tags: ["autodocs"], + args: { + children: "标签", + }, +} satisfies Meta<typeof Badge>; + +export default meta; +type Story = StoryObj<typeof meta>; + +export const Default: Story = {}; + +export const Variants: Story = { + render: () => ( + <div className="flex flex-wrap gap-2"> + <Badge>default</Badge> + <Badge variant="secondary">secondary</Badge> + <Badge variant="outline">outline</Badge> + <Badge variant="destructive">destructive</Badge> + </div> + ), +}; diff --git a/packages/ui/stories/button.stories.tsx b/packages/ui/stories/button.stories.tsx new file mode 100644 index 0000000..ecf33f4 --- /dev/null +++ b/packages/ui/stories/button.stories.tsx @@ -0,0 +1,49 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Button } from "../src/index.js"; + +const meta = { + title: "Basic/Button", + component: Button, + tags: ["autodocs"], + args: { + children: "按钮", + }, +} satisfies Meta<typeof Button>; + +export default meta; +type Story = StoryObj<typeof meta>; + +export const Default: Story = {}; + +export const Variants: Story = { + render: () => ( + <div className="flex flex-wrap gap-2"> + <Button variant="default">Default</Button> + <Button variant="secondary">Secondary</Button> + <Button variant="outline">Outline</Button> + <Button variant="ghost">Ghost</Button> + <Button variant="destructive">Destructive</Button> + <Button variant="link">Link</Button> + </div> + ), +}; + +export const Sizes: Story = { + render: () => ( + <div className="flex flex-wrap items-center gap-2"> + <Button size="sm">小</Button> + <Button size="default">默认</Button> + <Button size="lg">大</Button> + <Button size="icon" aria-label="图标按钮"> + ⌘ + </Button> + </div> + ), +}; + +export const Disabled: Story = { + args: { + disabled: true, + children: "禁用", + }, +}; diff --git a/packages/ui/stories/card.stories.tsx b/packages/ui/stories/card.stories.tsx new file mode 100644 index 0000000..f383468 --- /dev/null +++ b/packages/ui/stories/card.stories.tsx @@ -0,0 +1,39 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { + Button, + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "../src/index.js"; + +const meta = { + title: "Basic/Card", + component: Card, + tags: ["autodocs"], +} satisfies Meta<typeof Card>; + +export default meta; +type Story = StoryObj<typeof meta>; + +export const Default: Story = { + render: () => ( + <Card className="w-[360px]"> + <CardHeader> + <CardTitle>卡片标题</CardTitle> + <CardDescription>一句描述说明这个卡片的用途。</CardDescription> + </CardHeader> + <CardContent> + <p className="text-sm">正文内容区域,可放表单、列表或摘要。</p> + </CardContent> + <CardFooter className="gap-2"> + <Button size="sm">主要操作</Button> + <Button size="sm" variant="ghost"> + 次要操作 + </Button> + </CardFooter> + </Card> + ), +}; diff --git a/packages/ui/stories/dropdown-menu.stories.tsx b/packages/ui/stories/dropdown-menu.stories.tsx new file mode 100644 index 0000000..14d1ba1 --- /dev/null +++ b/packages/ui/stories/dropdown-menu.stories.tsx @@ -0,0 +1,37 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { + Button, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "../src/index.js"; + +const meta = { + title: "Basic/DropdownMenu", + component: DropdownMenu, + tags: ["autodocs"], +} satisfies Meta<typeof DropdownMenu>; + +export default meta; +type Story = StoryObj<typeof meta>; + +export const Default: Story = { + render: () => ( + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button variant="outline">打开菜单</Button> + </DropdownMenuTrigger> + <DropdownMenuContent align="start" className="w-48"> + <DropdownMenuLabel>我的账户</DropdownMenuLabel> + <DropdownMenuSeparator /> + <DropdownMenuItem>个人资料</DropdownMenuItem> + <DropdownMenuItem>设置</DropdownMenuItem> + <DropdownMenuSeparator /> + <DropdownMenuItem>退出登录</DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + ), +}; diff --git a/packages/ui/stories/input.stories.tsx b/packages/ui/stories/input.stories.tsx new file mode 100644 index 0000000..32f20c1 --- /dev/null +++ b/packages/ui/stories/input.stories.tsx @@ -0,0 +1,30 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Input } from "../src/index.js"; + +const meta = { + title: "Basic/Input", + component: Input, + tags: ["autodocs"], + args: { + placeholder: "请输入…", + }, +} satisfies Meta<typeof Input>; + +export default meta; +type Story = StoryObj<typeof meta>; + +export const Default: Story = {}; + +export const Disabled: Story = { + args: { + disabled: true, + value: "禁用态", + }, +}; + +export const WithType: Story = { + args: { + type: "email", + placeholder: "input@example.com", + }, +}; diff --git a/packages/ui/stories/label.stories.tsx b/packages/ui/stories/label.stories.tsx new file mode 100644 index 0000000..ccb481f --- /dev/null +++ b/packages/ui/stories/label.stories.tsx @@ -0,0 +1,25 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Input, Label } from "../src/index.js"; + +const meta = { + title: "Basic/Label", + component: Label, + tags: ["autodocs"], + args: { + children: "字段名称", + }, +} satisfies Meta<typeof Label>; + +export default meta; +type Story = StoryObj<typeof meta>; + +export const Default: Story = {}; + +export const WithInput: Story = { + render: () => ( + <div className="grid w-[280px] gap-2"> + <Label htmlFor="email">邮箱</Label> + <Input id="email" type="email" placeholder="input@example.com" /> + </div> + ), +}; diff --git a/packages/ui/stories/separator.stories.tsx b/packages/ui/stories/separator.stories.tsx new file mode 100644 index 0000000..604b592 --- /dev/null +++ b/packages/ui/stories/separator.stories.tsx @@ -0,0 +1,23 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Separator } from "../src/index.js"; + +const meta = { + title: "Basic/Separator", + component: Separator, + tags: ["autodocs"], +} satisfies Meta<typeof Separator>; + +export default meta; +type Story = StoryObj<typeof meta>; + +export const Horizontal: Story = {}; + +export const Vertical: Story = { + render: () => ( + <div className="flex h-16 items-center gap-4"> + <span className="text-sm">左</span> + <Separator orientation="vertical" /> + <span className="text-sm">右</span> + </div> + ), +}; diff --git a/packages/ui/stories/sonner.stories.tsx b/packages/ui/stories/sonner.stories.tsx new file mode 100644 index 0000000..c78c233 --- /dev/null +++ b/packages/ui/stories/sonner.stories.tsx @@ -0,0 +1,38 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Button, Toaster, toast } from "../src/index.js"; + +const meta = { + title: "Basic/Toaster", + component: Toaster, + tags: ["autodocs"], +} satisfies Meta<typeof Toaster>; + +export default meta; +type Story = StoryObj<typeof meta>; + +export const Default: Story = { + render: () => ( + <div className="flex flex-wrap gap-2"> + <Toaster position="top-center" /> + <Button + onClick={() => + toast.success("保存成功", { + description: "更改已写入本地状态。", + }) + } + > + 成功 toast + </Button> + <Button + variant="destructive" + onClick={() => + toast.error("操作失败", { + description: "请检查网络后重试。", + }) + } + > + 失败 toast + </Button> + </div> + ), +}; diff --git a/packages/ui/stories/table.stories.tsx b/packages/ui/stories/table.stories.tsx new file mode 100644 index 0000000..5ef664d --- /dev/null +++ b/packages/ui/stories/table.stories.tsx @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { + Table, + TableBody, + TableCaption, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "../src/index.js"; + +const meta = { + title: "Basic/Table", + component: Table, + tags: ["autodocs"], +} satisfies Meta<typeof Table>; + +export default meta; +type Story = StoryObj<typeof meta>; + +export const Default: Story = { + render: () => ( + <Table> + <TableCaption>示例数据表</TableCaption> + <TableHeader> + <TableRow> + <TableHead className="w-[100px]">ID</TableHead> + <TableHead>名称</TableHead> + <TableHead>状态</TableHead> + </TableRow> + </TableHeader> + <TableBody> + <TableRow> + <TableCell className="font-mono text-xs">a1b2c3d4</TableCell> + <TableCell>应用模板</TableCell> + <TableCell>活跃</TableCell> + </TableRow> + <TableRow> + <TableCell className="font-mono text-xs">e5f6g7h8</TableCell> + <TableCell>组件库</TableCell> + <TableCell>草稿</TableCell> + </TableRow> + </TableBody> + </Table> + ), +}; diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json new file mode 100644 index 0000000..4e98841 --- /dev/null +++ b/packages/ui/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "jsx": "react-jsx", + "jsxImportSource": "react", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "paths": {} + }, + "include": ["src", "stories", ".storybook"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..567a2df --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022"], + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "exactOptionalPropertyTypes": true, + "noPropertyAccessFromIndexSignature": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true + }, + "exclude": ["node_modules", "dist"] +} diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..9149789 --- /dev/null +++ b/turbo.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://turbo.build/schema.json", + "globalDependencies": ["tsconfig.base.json", "biome.json"], + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", "*.tsbuildinfo"], + "inputs": [ + "src/**", + "scripts/**", + "package.json", + "tsconfig*.json", + "vite.config.*", + "astro.config.*" + ], + "env": ["PUBLIC_*", "NODE_ENV", "ASTRO_*"] + }, + "dev": { + "cache": false, + "persistent": true, + "dependsOn": ["^build"] + }, + "typecheck": { + "dependsOn": ["^build"], + "outputs": ["*.tsbuildinfo"] + }, + "lint": { + "outputs": [] + }, + "db:generate": { + "cache": false, + "dependsOn": ["^build"] + }, + "db:push": { + "cache": false, + "dependsOn": ["^build"] + }, + "db:studio": { + "cache": false, + "persistent": true, + "dependsOn": ["^build"] + }, + "storybook": { + "cache": false, + "persistent": true, + "dependsOn": ["^build"] + }, + "build-storybook": { + "dependsOn": ["^build"], + "outputs": ["storybook-static/**"] + } + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..a5a9247 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,9655 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 10 + cacheKey: 10c0 + +"@adobe/css-tools@npm:^4.4.0": + version: 4.5.0 + resolution: "@adobe/css-tools@npm:4.5.0" + checksum: 10c0/fc969e1117098eb4cccdb73beb2508daa0e52760af1183d6288bafea59204943490ab3ede28593032ffb8929c0cee270b2a53254fe61139ab00604ea8fc33cea + languageName: node + linkType: hard + +"@app/adsense@workspace:packages/adsense": + version: 0.0.0-use.local + resolution: "@app/adsense@workspace:packages/adsense" + dependencies: + "@types/react": "npm:^19.1.4" + react: "npm:^19.1.0" + typescript: "npm:^7.0.2" + peerDependencies: + react: ">=18" + languageName: unknown + linkType: soft + +"@app/dao@workspace:*, @app/dao@workspace:packages/dao": + version: 0.0.0-use.local + resolution: "@app/dao@workspace:packages/dao" + dependencies: + "@app/db": "workspace:*" + "@app/models": "workspace:*" + "@types/node": "npm:^22.15.29" + drizzle-orm: "npm:^0.45.3" + typescript: "npm:^7.0.2" + languageName: unknown + linkType: soft + +"@app/db@workspace:*, @app/db@workspace:packages/db": + version: 0.0.0-use.local + resolution: "@app/db@workspace:packages/db" + dependencies: + "@app/models": "workspace:*" + "@libsql/client": "npm:^0.14.0" + "@types/node": "npm:^22.15.29" + drizzle-kit: "npm:^0.30.2" + drizzle-orm: "npm:^0.45.3" + typescript: "npm:^7.0.2" + languageName: unknown + linkType: soft + +"@app/design-tokens@workspace:*, @app/design-tokens@workspace:packages/design-tokens": + version: 0.0.0-use.local + resolution: "@app/design-tokens@workspace:packages/design-tokens" + languageName: unknown + linkType: soft + +"@app/i18n@workspace:*, @app/i18n@workspace:packages/i18n": + version: 0.0.0-use.local + resolution: "@app/i18n@workspace:packages/i18n" + dependencies: + "@types/node": "npm:^22.15.29" + typescript: "npm:^7.0.2" + languageName: unknown + linkType: soft + +"@app/models@workspace:*, @app/models@workspace:packages/models": + version: 0.0.0-use.local + resolution: "@app/models@workspace:packages/models" + dependencies: + "@types/node": "npm:^22.15.29" + drizzle-orm: "npm:^0.45.3" + typescript: "npm:^7.0.2" + languageName: unknown + linkType: soft + +"@app/seo-geo@workspace:*, @app/seo-geo@workspace:packages/seo-geo": + version: 0.0.0-use.local + resolution: "@app/seo-geo@workspace:packages/seo-geo" + dependencies: + "@types/mdast": "npm:^4.0.4" + "@types/serialize-javascript": "npm:^5.0.4" + "@unhead/schema-org": "npm:^3.4.1" + escape-html: "npm:^1.0.3" + mdast-util-to-markdown: "npm:^2.1.2" + schema-dts: "npm:^2.0.0" + serialize-javascript: "npm:^7.1.1" + sitemap: "npm:^9.0.1" + typescript: "npm:^7.0.2" + unhead: "npm:^3.4.1" + zod: "npm:^4.6.5" + languageName: unknown + linkType: soft + +"@app/trpc@workspace:*, @app/trpc@workspace:packages/trpc": + version: 0.0.0-use.local + resolution: "@app/trpc@workspace:packages/trpc" + dependencies: + "@app/dao": "workspace:*" + "@app/db": "workspace:*" + "@app/models": "workspace:*" + "@app/types": "workspace:*" + "@trpc/server": "npm:^11.19.0" + "@types/node": "npm:^22.15.29" + drizzle-orm: "npm:^0.45.3" + tsx: "npm:^4.19.4" + typescript: "npm:^7.0.2" + vite: "npm:^8.3.0" + languageName: unknown + linkType: soft + +"@app/types@workspace:*, @app/types@workspace:packages/types": + version: 0.0.0-use.local + resolution: "@app/types@workspace:packages/types" + dependencies: + typescript: "npm:^7.0.2" + zod: "npm:^4.6.5" + languageName: unknown + linkType: soft + +"@app/ui@workspace:*, @app/ui@workspace:packages/ui": + version: 0.0.0-use.local + resolution: "@app/ui@workspace:packages/ui" + dependencies: + "@app/design-tokens": "workspace:*" + "@biomejs/biome": "npm:^2.1.1" + "@radix-ui/react-avatar": "npm:^1.2.6" + "@radix-ui/react-dropdown-menu": "npm:^2.1.24" + "@radix-ui/react-label": "npm:^2.1.15" + "@radix-ui/react-separator": "npm:^1.1.15" + "@storybook/addon-a11y": "npm:^9.1.10" + "@storybook/addon-docs": "npm:^9.1.10" + "@storybook/react": "npm:^9.1.10" + "@storybook/react-vite": "npm:^9.1.10" + "@tailwindcss/vite": "npm:^4.3.3" + "@types/react": "npm:^19.1.4" + "@types/react-dom": "npm:^19.1.5" + class-variance-authority: "npm:^0.7.1" + clsx: "npm:^2.1.1" + react: "npm:^19.1.0" + react-dom: "npm:^19.1.0" + sonner: "npm:^2.0.8" + storybook: "npm:^9.1.10" + tailwind-merge: "npm:^3.7.0" + tailwindcss: "npm:^4.3.3" + typescript: "npm:^7.0.2" + vite: "npm:^7.1.0" + peerDependencies: + react: ">=18" + react-dom: ">=18" + languageName: unknown + linkType: soft + +"@app/web@workspace:app/web": + version: 0.0.0-use.local + resolution: "@app/web@workspace:app/web" + dependencies: + "@app/design-tokens": "workspace:*" + "@app/i18n": "workspace:*" + "@app/seo-geo": "workspace:*" + "@app/trpc": "workspace:*" + "@app/types": "workspace:*" + "@app/ui": "workspace:*" + "@astrojs/check": "npm:^0.9.10" + "@astrojs/node": "npm:^11.1.6" + "@astrojs/react": "npm:^4.3.0" + "@biomejs/biome": "npm:^2.1.1" + "@tailwindcss/vite": "npm:^4.3.3" + "@tanstack/react-query": "npm:^5.75.0" + "@trpc/client": "npm:^11.19.0" + "@trpc/react-query": "npm:^11.19.0" + "@trpc/server": "npm:^11.19.0" + "@types/react": "npm:^19.1.4" + "@types/react-dom": "npm:^19.1.5" + astro: "npm:^7.3.3" + gsap: "npm:^3.15.0" + lenis: "npm:^1.3.26" + react: "npm:^19.1.0" + react-dom: "npm:^19.1.0" + tailwindcss: "npm:^4.3.3" + typescript: "npm:^7.0.2" + languageName: unknown + linkType: soft + +"@astrojs/astro2tsx@npm:^0.1.0": + version: 0.1.1 + resolution: "@astrojs/astro2tsx@npm:0.1.1" + dependencies: + "@emnapi/core": "npm:1.11.3" + "@napi-rs/wasm-runtime": "npm:1.2.4" + checksum: 10c0/8b744cd6dd60d3f66f2c7a1a65d0da138f01b330488c5abaf1b01e263c410026467a4c4644e5105eb2789c383a9ead0609bb3e67b686dd74d89e91c89a39f18c + languageName: node + linkType: hard + +"@astrojs/check@npm:^0.9.10": + version: 0.9.10 + resolution: "@astrojs/check@npm:0.9.10" + dependencies: + "@astrojs/language-server": "npm:^2.16.7" + chokidar: "npm:^4.0.3" + kleur: "npm:^4.1.5" + yargs: "npm:^18.0.0" + peerDependencies: + typescript: ^5.0.0 || ^6.0.0 + bin: + astro-check: ./bin/astro-check.js + checksum: 10c0/a5de46d71e2c295bbee3c575af73b015e070c2a418f1bc1196b115fddacf60681b5313d9f6ea0427cec5f138bdf6d3c894d8e1938fd54025624b7bab2f52e7eb + languageName: node + linkType: hard + +"@astrojs/compiler-binding-android-arm64@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/compiler-binding-android-arm64@npm:0.4.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@astrojs/compiler-binding-darwin-arm64@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/compiler-binding-darwin-arm64@npm:0.4.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@astrojs/compiler-binding-darwin-x64@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/compiler-binding-darwin-x64@npm:0.4.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@astrojs/compiler-binding-linux-arm64-gnu@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/compiler-binding-linux-arm64-gnu@npm:0.4.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@astrojs/compiler-binding-linux-arm64-musl@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/compiler-binding-linux-arm64-musl@npm:0.4.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@astrojs/compiler-binding-linux-x64-gnu@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/compiler-binding-linux-x64-gnu@npm:0.4.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@astrojs/compiler-binding-linux-x64-musl@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/compiler-binding-linux-x64-musl@npm:0.4.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@astrojs/compiler-binding-wasm32-wasi@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/compiler-binding-wasm32-wasi@npm:0.4.1" + dependencies: + "@napi-rs/wasm-runtime": "npm:^1.2.4" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@astrojs/compiler-binding-win32-arm64-msvc@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/compiler-binding-win32-arm64-msvc@npm:0.4.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@astrojs/compiler-binding-win32-x64-msvc@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/compiler-binding-win32-x64-msvc@npm:0.4.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@astrojs/compiler-binding@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/compiler-binding@npm:0.4.1" + dependencies: + "@astrojs/compiler-binding-android-arm64": "npm:0.4.1" + "@astrojs/compiler-binding-darwin-arm64": "npm:0.4.1" + "@astrojs/compiler-binding-darwin-x64": "npm:0.4.1" + "@astrojs/compiler-binding-linux-arm64-gnu": "npm:0.4.1" + "@astrojs/compiler-binding-linux-arm64-musl": "npm:0.4.1" + "@astrojs/compiler-binding-linux-x64-gnu": "npm:0.4.1" + "@astrojs/compiler-binding-linux-x64-musl": "npm:0.4.1" + "@astrojs/compiler-binding-wasm32-wasi": "npm:0.4.1" + "@astrojs/compiler-binding-win32-arm64-msvc": "npm:0.4.1" + "@astrojs/compiler-binding-win32-x64-msvc": "npm:0.4.1" + dependenciesMeta: + "@astrojs/compiler-binding-android-arm64": + optional: true + "@astrojs/compiler-binding-darwin-arm64": + optional: true + "@astrojs/compiler-binding-darwin-x64": + optional: true + "@astrojs/compiler-binding-linux-arm64-gnu": + optional: true + "@astrojs/compiler-binding-linux-arm64-musl": + optional: true + "@astrojs/compiler-binding-linux-x64-gnu": + optional: true + "@astrojs/compiler-binding-linux-x64-musl": + optional: true + "@astrojs/compiler-binding-wasm32-wasi": + optional: true + "@astrojs/compiler-binding-win32-arm64-msvc": + optional: true + "@astrojs/compiler-binding-win32-x64-msvc": + optional: true + checksum: 10c0/75ab9b1806f2941165a17ca8e5228037fd23f30e75861a3c1d5fb9c563ce38fd46aa20e1bc72868bcdf804eeecbdff2a85a5a98f4dddc71d155ef650e8496267 + languageName: node + linkType: hard + +"@astrojs/compiler-rs@npm:^0.4.0": + version: 0.4.1 + resolution: "@astrojs/compiler-rs@npm:0.4.1" + dependencies: + "@astrojs/compiler-binding": "npm:0.4.1" + checksum: 10c0/3c6859b40baffabc257544ce3ea1e5120432676a0ff207ed316df1a3024e1f20445716d742133c7c89f2c675156af72d2bc0e82d670066d855b4eb9c8fc02173 + languageName: node + linkType: hard + +"@astrojs/internal-helpers@npm:0.11.0": + version: 0.11.0 + resolution: "@astrojs/internal-helpers@npm:0.11.0" + dependencies: + "@types/hast": "npm:^3.0.4" + "@types/mdast": "npm:^4.0.4" + js-yaml: "npm:^4.3.0" + picomatch: "npm:^4.0.4" + retext-smartypants: "npm:^6.2.0" + shiki: "npm:^4.0.2" + smol-toml: "npm:^1.6.0" + unified: "npm:^11.0.5" + checksum: 10c0/ec7bf3dcd81ee1c6b1baa4d599a92c3aa97da21c875c1527576812f5bfa84db3d459726443467933a7e243efb8ff6ac32265f5971f314a8b5ff7a73eef451289 + languageName: node + linkType: hard + +"@astrojs/language-server@npm:^2.16.7": + version: 2.17.0 + resolution: "@astrojs/language-server@npm:2.17.0" + dependencies: + "@astrojs/astro2tsx": "npm:^0.1.0" + "@astrojs/yaml2ts": "npm:^0.2.4" + "@volar/kit": "npm:~2.4.28" + "@volar/language-core": "npm:~2.4.28" + "@volar/language-server": "npm:~2.4.28" + "@volar/language-service": "npm:~2.4.28" + muggle-string: "npm:^0.4.1" + tinyglobby: "npm:^0.2.16" + volar-service-css: "npm:0.0.71" + volar-service-emmet: "npm:0.0.71" + volar-service-html: "npm:0.0.71" + volar-service-prettier: "npm:0.0.71" + volar-service-typescript: "npm:0.0.71" + volar-service-typescript-twoslash-queries: "npm:0.0.71" + volar-service-yaml: "npm:0.0.71" + vscode-html-languageservice: "npm:^5.6.2" + vscode-uri: "npm:^3.1.0" + peerDependencies: + prettier: ^3.0.0 + prettier-plugin-astro: ">=0.11.0" + peerDependenciesMeta: + prettier: + optional: true + prettier-plugin-astro: + optional: true + bin: + astro-ls: ./bin/nodeServer.js + checksum: 10c0/4b284f5ede301400b9c719ab00a0a2aa447a5f5f06b55b98edccf06161ec1e684ee8f9ff2e2ee6f470ed8913c66e1f98186f685e4d01e3f93fad4c4cb3f2d828 + languageName: node + linkType: hard + +"@astrojs/markdown-satteri@npm:0.4.1": + version: 0.4.1 + resolution: "@astrojs/markdown-satteri@npm:0.4.1" + dependencies: + "@astrojs/internal-helpers": "npm:0.11.0" + "@astrojs/prism": "npm:4.0.2" + github-slugger: "npm:^2.0.0" + satteri: "npm:^0.10.3" + checksum: 10c0/04b8ec777ee6e19c212887f022b151f3348a4150c1f66ab33d4927e986722820b9b1c2b6b10c3a45721b11eef429ab33cfca1b342b11b5d040e0a90e79a8f83e + languageName: node + linkType: hard + +"@astrojs/node@npm:^11.1.6": + version: 11.1.6 + resolution: "@astrojs/node@npm:11.1.6" + dependencies: + "@astrojs/internal-helpers": "npm:0.11.0" + send: "npm:^1.2.1" + server-destroy: "npm:^1.0.1" + peerDependencies: + astro: ^7.2.1 + checksum: 10c0/5ada50db447180f239141572e0fad48a435ddf0055f7da2b0c47c7784d86082fc271aaf09d2ec4526a1f789562f216a994a48bb701c391a1b3faa1ace9804c54 + languageName: node + linkType: hard + +"@astrojs/prism@npm:4.0.2": + version: 4.0.2 + resolution: "@astrojs/prism@npm:4.0.2" + dependencies: + prismjs: "npm:^1.30.0" + checksum: 10c0/23a5bc096f966c08be6f8e89adfe9287fd6d08eeb9c4ec7de8172280aa638f2684dcefc5f848f55e211ff63369abc4f0661698daa5e2de6078c8f269215a7101 + languageName: node + linkType: hard + +"@astrojs/react@npm:^4.3.0": + version: 4.4.2 + resolution: "@astrojs/react@npm:4.4.2" + dependencies: + "@vitejs/plugin-react": "npm:^4.7.0" + ultrahtml: "npm:^1.6.0" + vite: "npm:^6.4.1" + peerDependencies: + "@types/react": ^17.0.50 || ^18.0.21 || ^19.0.0 + "@types/react-dom": ^17.0.17 || ^18.0.6 || ^19.0.0 + react: ^17.0.2 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0 + checksum: 10c0/aabe71afcba01b1a133c97201c7dcdb417c2345ad73b0bae7d331651fcec9a6b0f2c6eb0c18b600973582065d608676b026f0fb8dd38c83dcbb82cd1377cfd7f + languageName: node + linkType: hard + +"@astrojs/telemetry@npm:3.3.3": + version: 3.3.3 + resolution: "@astrojs/telemetry@npm:3.3.3" + dependencies: + ci-info: "npm:^4.4.0" + dset: "npm:^3.1.4" + is-docker: "npm:^4.0.0" + package-manager-detector: "npm:^1.6.0" + checksum: 10c0/d5373e0aa8cbfd684143027dce4e31f7fd45e90df799ce4d255fb750109173a3dd2fc3ef26532b298c715fe063d6e955984d238e323d96e1d8f6c0a03fbbbc73 + languageName: node + linkType: hard + +"@astrojs/yaml2ts@npm:^0.2.4": + version: 0.2.4 + resolution: "@astrojs/yaml2ts@npm:0.2.4" + dependencies: + yaml: "npm:^2.8.3" + checksum: 10c0/bb0a25ae730ea2b8ba20da4626c9d023ba27898396c2ad71384e0aae10c4479bb5d6e9f4f27aad1f486f1d287200c4753ff949ab16af367788f085b078d4b4e6 + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/code-frame@npm:7.29.7" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.29.7" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/169fc2080169a40c1760155eaaaf739bcb882df0bec76a83adbda5493645bc17270a3434b8848c494b1933e96fe1d147370001e3cda09a39f43ae30f08ef2069 + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/compat-data@npm:7.29.7" + checksum: 10c0/47913f05e08a45a1c9df38c02b4b49e391005085b489432647a1abe112e5d9c75e3be8ea5972b7f6da4ec5d1339922ceb9ea02b8a25d4ed1cb8636e5261f344e + languageName: node + linkType: hard + +"@babel/core@npm:^7.28.0": + version: 7.29.7 + resolution: "@babel/core@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-compilation-targets": "npm:^7.29.7" + "@babel/helper-module-transforms": "npm:^7.29.7" + "@babel/helpers": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + "@jridgewell/remapping": "npm:^2.3.5" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10c0/112fb09c24de7a1de64d1de2c31fe65c4e6af4cb2fb6e6d99ea5373e6fc51e75b88581c0efae4c4c68f119a02a988c7106e95011a41530a2fb8ed793c7eaa07b + languageName: node + linkType: hard + +"@babel/generator@npm:^7.29.7, @babel/generator@npm:^7.29.8": + version: 7.29.8 + resolution: "@babel/generator@npm:7.29.8" + dependencies: + "@babel/parser": "npm:^7.29.8" + "@babel/types": "npm:^7.29.8" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" + jsesc: "npm:^3.0.2" + checksum: 10c0/7b896696314a659652393b76d78276e236acd0f7fae40a9a1af7f01c76aeafc630dd0966aad6f9386d35d7c674a8e6e2d8e217c44d25fb11460e68afa9ba8441 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-compilation-targets@npm:7.29.7" + dependencies: + "@babel/compat-data": "npm:^7.29.7" + "@babel/helper-validator-option": "npm:^7.29.7" + browserslist: "npm:^4.24.0" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 10c0/4c15fd4c69a0a7047799a28a88460c19cede0a0ee8af994ea169114986f4af48b92c7393a4a3fee0456c11a656eece3448a6ed06354453d6c27cccf17195453b + languageName: node + linkType: hard + +"@babel/helper-globals@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-globals@npm:7.29.7" + checksum: 10c0/f38417c40b1129a1b2b519ca961b9040c8827d1444fd74068702286b91b77089431dc76b6b9d5c1496e5da2a4f3ad329c6946e688ba3fa0d1d0b3d2b4f34f36a + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-imports@npm:7.29.7" + dependencies: + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/6adf60d97356027413342a092f818d9678c4f5caff716a33e3284b5ae14e47a9e88059d421dde4ee4894691260039a12602c0e7becadc175602194b40dfa345d + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-transforms@npm:7.29.7" + dependencies: + "@babel/helper-module-imports": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/ee5a2172c24a42be696836f4b0d947489c9729d8adf5821885cf77d1ad5333e3c447368e9a71f67df1099570490553dccf9f888ef0a92a48aa63cb086bd8c7e1 + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-plugin-utils@npm:7.29.7" + checksum: 10c0/380477a06133274a2759f9355929cb60a95e8b8fee624a1ae1fa349e1d1645b89daca456f72833f6d1062bffa12ee4271c5bf0cc5a61c0166cdc24c7591e2408 + languageName: node + linkType: hard + +"@babel/helper-string-parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-string-parser@npm:7.29.7" + checksum: 10c0/194bc0f1716e396d5ffde56ad6119745fb9557662c98611590e5e454906783a4ccb21ce93056b8eb69a4909044834e45d96e50ac695bbe9e3221648fe033c06c + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-identifier@npm:7.29.7" + checksum: 10c0/4795354e7ae0dcafa72de1cd04ec51252dc1498517170beaf019e03effc5b7bf13c6b21a3949a77e07b8125be7f106ed1131350d8ebd4566ae874094a726d62b + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-option@npm:7.29.7" + checksum: 10c0/d2a06c6d0ac40ba4a2f219fc2cab249c7a94bacdb2686273b7f9598571c908809b48468ff588915a346e6cc7296f60b581023d1d498b747fed06f779d335c2cc + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helpers@npm:7.29.7" + dependencies: + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/218e8d10953647c9f44775f5a022b227a182674853b5ea8631889deb7e1a3e4bc870388aaecf59bb8bd92a87f9a96220ed3f70a35bffec6bcf9169ecb67891ac + languageName: node + linkType: hard + +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.29.7, @babel/parser@npm:^7.29.8": + version: 7.29.9 + resolution: "@babel/parser@npm:7.29.9" + dependencies: + "@babel/types": "npm:^7.29.8" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/171b16eb4527cdbbb72cb98710f91acb22356e2c40196ba3b7a1b84efa8c55d0b0992f399c8c675c3e17f67fd133676c5c4118e5f20db243f427c8b29b0c3f40 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx-self@npm:^7.27.1": + version: 7.29.7 + resolution: "@babel/plugin-transform-react-jsx-self@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/288995f0fd0d61ab740a315fb56c8255eb87dd4a4ac2ac7d0fdd4ce173c3878200141e80da2db0e598c7b2a71e74e604afdbb4c8e14ae6e0527ce0b6294c03da + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx-source@npm:^7.27.1": + version: 7.29.7 + resolution: "@babel/plugin-transform-react-jsx-source@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/a121899631e6d99b9e1b276acf736dbb77948a31f8eeeae67b89c8a4ab0f05e51ba64544baa06c286a2b9944f227244e15aac464e2313d286d0511fe51e27975 + languageName: node + linkType: hard + +"@babel/template@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/template@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/8bb7f900dcab0e9e1c5ffbc33ca10e0d26b7b2e2ca804becb73ee771b9c4ed6e2908a4ae4a14c08560febb45d2b6b9a173955e42ad404d05f8b04840a14d9c58 + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.29.7": + version: 7.29.8 + resolution: "@babel/traverse@npm:7.29.8" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.8" + "@babel/helper-globals": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.8" + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.8" + debug: "npm:^4.3.1" + checksum: 10c0/87a28989c434add26d787776ac6d30f749b89cb030f2a605c89f671a516a6fa165ac0476f07e2b69feed70128b2734cae1cbbf41dfe95ccf22081bd8f8b91923 + languageName: node + linkType: hard + +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.28.2, @babel/types@npm:^7.29.7, @babel/types@npm:^7.29.8": + version: 7.29.8 + resolution: "@babel/types@npm:7.29.8" + dependencies: + "@babel/helper-string-parser": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + checksum: 10c0/be7c279f0abf2a086c633e21b49c7ca80275d05283cc5a268b67a708c9914bd0c944f1422b3eb3cb37682a2af5d560abf520ccf9b01b53ecbfe6b71fbc3fdde6 + languageName: node + linkType: hard + +"@biomejs/biome@npm:^2.1.1": + version: 2.5.14 + resolution: "@biomejs/biome@npm:2.5.14" + dependencies: + "@biomejs/cli-darwin-arm64": "npm:2.5.14" + "@biomejs/cli-darwin-x64": "npm:2.5.14" + "@biomejs/cli-linux-arm64": "npm:2.5.14" + "@biomejs/cli-linux-arm64-musl": "npm:2.5.14" + "@biomejs/cli-linux-x64": "npm:2.5.14" + "@biomejs/cli-linux-x64-musl": "npm:2.5.14" + "@biomejs/cli-win32-arm64": "npm:2.5.14" + "@biomejs/cli-win32-x64": "npm:2.5.14" + dependenciesMeta: + "@biomejs/cli-darwin-arm64": + optional: true + "@biomejs/cli-darwin-x64": + optional: true + "@biomejs/cli-linux-arm64": + optional: true + "@biomejs/cli-linux-arm64-musl": + optional: true + "@biomejs/cli-linux-x64": + optional: true + "@biomejs/cli-linux-x64-musl": + optional: true + "@biomejs/cli-win32-arm64": + optional: true + "@biomejs/cli-win32-x64": + optional: true + bin: + biome: bin/biome + checksum: 10c0/db612f71783a33a11666d09febe8310993474671ca41191ae9c0fa7bfe96f7f6814a5f57dea445e70a88991d8d09a6c5212135aa2950158f320824e4f48ec0a4 + languageName: node + linkType: hard + +"@biomejs/cli-darwin-arm64@npm:2.5.14": + version: 2.5.14 + resolution: "@biomejs/cli-darwin-arm64@npm:2.5.14" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@biomejs/cli-darwin-x64@npm:2.5.14": + version: 2.5.14 + resolution: "@biomejs/cli-darwin-x64@npm:2.5.14" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@biomejs/cli-linux-arm64-musl@npm:2.5.14": + version: 2.5.14 + resolution: "@biomejs/cli-linux-arm64-musl@npm:2.5.14" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@biomejs/cli-linux-arm64@npm:2.5.14": + version: 2.5.14 + resolution: "@biomejs/cli-linux-arm64@npm:2.5.14" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@biomejs/cli-linux-x64-musl@npm:2.5.14": + version: 2.5.14 + resolution: "@biomejs/cli-linux-x64-musl@npm:2.5.14" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@biomejs/cli-linux-x64@npm:2.5.14": + version: 2.5.14 + resolution: "@biomejs/cli-linux-x64@npm:2.5.14" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@biomejs/cli-win32-arm64@npm:2.5.14": + version: 2.5.14 + resolution: "@biomejs/cli-win32-arm64@npm:2.5.14" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@biomejs/cli-win32-x64@npm:2.5.14": + version: 2.5.14 + resolution: "@biomejs/cli-win32-x64@npm:2.5.14" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@bruits/satteri-darwin-arm64@npm:0.10.5": + version: 0.10.5 + resolution: "@bruits/satteri-darwin-arm64@npm:0.10.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@bruits/satteri-darwin-x64@npm:0.10.5": + version: 0.10.5 + resolution: "@bruits/satteri-darwin-x64@npm:0.10.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@bruits/satteri-linux-arm64-gnu@npm:0.10.5": + version: 0.10.5 + resolution: "@bruits/satteri-linux-arm64-gnu@npm:0.10.5" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@bruits/satteri-linux-arm64-musl@npm:0.10.5": + version: 0.10.5 + resolution: "@bruits/satteri-linux-arm64-musl@npm:0.10.5" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@bruits/satteri-linux-x64-gnu@npm:0.10.5": + version: 0.10.5 + resolution: "@bruits/satteri-linux-x64-gnu@npm:0.10.5" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@bruits/satteri-linux-x64-musl@npm:0.10.5": + version: 0.10.5 + resolution: "@bruits/satteri-linux-x64-musl@npm:0.10.5" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@bruits/satteri-wasm32-wasi@npm:0.10.5": + version: 0.10.5 + resolution: "@bruits/satteri-wasm32-wasi@npm:0.10.5" + dependencies: + "@emnapi/core": "npm:1.11.1" + "@emnapi/runtime": "npm:1.11.1" + "@napi-rs/wasm-runtime": "npm:^1.2.3" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@bruits/satteri-win32-arm64-msvc@npm:0.10.5": + version: 0.10.5 + resolution: "@bruits/satteri-win32-arm64-msvc@npm:0.10.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@bruits/satteri-win32-x64-msvc@npm:0.10.5": + version: 0.10.5 + resolution: "@bruits/satteri-win32-x64-msvc@npm:0.10.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@capsizecss/unpack@npm:^4.0.0": + version: 4.0.1 + resolution: "@capsizecss/unpack@npm:4.0.1" + dependencies: + fontkitten: "npm:^1.0.3" + checksum: 10c0/72ce03871e137bae1c4925f9dc916a6250cf9b9630a8bb28d2ef7575d20fd7ef0e8966b361aaa3a54c0e8d5ff40b133490bd97a9beb6dc209529102947d392b1 + languageName: node + linkType: hard + +"@clack/core@npm:1.5.1": + version: 1.5.1 + resolution: "@clack/core@npm:1.5.1" + dependencies: + fast-wrap-ansi: "npm:^0.2.0" + sisteransi: "npm:^1.0.5" + checksum: 10c0/6dca93ff5a34bfe8c150367519bc40a847e9172cb1a78b18c88da0507cb6ad899debe1ad5cfcb9796e439108f12f003cf06fd002e2293bc85ff500975e45ff13 + languageName: node + linkType: hard + +"@clack/prompts@npm:^1.1.0": + version: 1.8.1 + resolution: "@clack/prompts@npm:1.8.1" + dependencies: + "@clack/core": "npm:1.5.1" + fast-string-width: "npm:^3.0.2" + fast-wrap-ansi: "npm:^0.2.0" + sisteransi: "npm:^1.0.5" + checksum: 10c0/ed38748acde0cb1e7ebdac1242470c78bd90380cbdde1e5560e0828881e3c74e2676de666086cf8183c8d4d3f1fc6e489f15a004f0d9f8ee294a3aa744de2093 + languageName: node + linkType: hard + +"@commitlint/cli@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/cli@npm:21.2.3" + dependencies: + "@commitlint/config-conventional": "npm:^21.2.3" + "@commitlint/format": "npm:^21.2.3" + "@commitlint/lint": "npm:^21.2.3" + "@commitlint/load": "npm:^21.2.3" + "@commitlint/read": "npm:^21.2.3" + "@commitlint/types": "npm:^21.2.3" + tinyexec: "npm:^1.0.0" + yargs: "npm:^18.0.0" + bin: + commitlint: cli.js + checksum: 10c0/544e86b5e5af0cbae5a55b5643dd9a8565b7f00e435378b46eb372a0b46adf330577b7d5c25abddbb30d4aa3ad78b114b2514ca936aeff3b310eb6e5f13dfc9c + languageName: node + linkType: hard + +"@commitlint/config-conventional@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/config-conventional@npm:21.2.3" + dependencies: + "@commitlint/types": "npm:^21.2.3" + conventional-changelog-conventionalcommits: "npm:^10.0.0" + checksum: 10c0/584a7b436d6f2aace31f9a35c48da213aa5eed01f7d21ca6db3800992e59c0f52894e54d2006f1c42739b8edce509387bd2cba783a5b18088db58da3b2afbf44 + languageName: node + linkType: hard + +"@commitlint/config-validator@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/config-validator@npm:21.2.3" + dependencies: + "@commitlint/types": "npm:^21.2.3" + ajv: "npm:^8.11.0" + checksum: 10c0/54641610bf9863ba085be7e806a34324e67b8ee8e3f4c12fe7c3f1116a385729eabd0d16f42823d60128bc6de99742e31112bc54c70ef45b5c439cad6fd95d8d + languageName: node + linkType: hard + +"@commitlint/ensure@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/ensure@npm:21.2.3" + dependencies: + "@commitlint/types": "npm:^21.2.3" + es-toolkit: "npm:^1.46.0" + checksum: 10c0/55314de93bc297624c62e10f68622928fde9b3a8e9522d0f8c1b54e31c86d5b1e98518682673651e7e5894be821d683099b23eb26aae300b0e7958ee62b340bc + languageName: node + linkType: hard + +"@commitlint/execute-rule@npm:^21.0.1": + version: 21.0.1 + resolution: "@commitlint/execute-rule@npm:21.0.1" + checksum: 10c0/84fd03f20e808b9f4b68193410d9be90c54b3599f81e82ac23847e90ccba4b52768cfd1ee637931a6ca90575eaefa6ed5fc43a5398d52a4e2d6bef6dd354227e + languageName: node + linkType: hard + +"@commitlint/format@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/format@npm:21.2.3" + dependencies: + "@commitlint/types": "npm:^21.2.3" + picocolors: "npm:^1.1.1" + checksum: 10c0/d87685e1e10d3338d4d1cab929af03d938070b1bfed79c16ef6fb54a95e1f5e11d56ed7ac188e332b0db8ab25302426087b2152a58334c09c6764497fd2b3465 + languageName: node + linkType: hard + +"@commitlint/is-ignored@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/is-ignored@npm:21.2.3" + dependencies: + "@commitlint/types": "npm:^21.2.3" + semver: "npm:^7.6.0" + checksum: 10c0/bf2b8a47149c537cb90bacc9dec12cfeae891597490e3919813deacaec3b64821268c2595c84327641b9b1e5988e2668362ecca4cdf011141deac1ad3956f56b + languageName: node + linkType: hard + +"@commitlint/lint@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/lint@npm:21.2.3" + dependencies: + "@commitlint/is-ignored": "npm:^21.2.3" + "@commitlint/parse": "npm:^21.2.3" + "@commitlint/rules": "npm:^21.2.3" + "@commitlint/types": "npm:^21.2.3" + checksum: 10c0/fe572e141f84b2a289e7c33356b8adaecc75d09e5e4cb07f173e0d80ea5f1070897536729fab5f1e0123e77a06982c97c3a417ae84b46877d7bd0913e382b647 + languageName: node + linkType: hard + +"@commitlint/load@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/load@npm:21.2.3" + dependencies: + "@commitlint/config-validator": "npm:^21.2.3" + "@commitlint/execute-rule": "npm:^21.0.1" + "@commitlint/resolve-extends": "npm:^21.2.3" + "@commitlint/types": "npm:^21.2.3" + cosmiconfig: "npm:^9.0.1" + cosmiconfig-typescript-loader: "npm:^6.1.0" + es-toolkit: "npm:^1.46.0" + is-plain-obj: "npm:^4.1.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/b79e17711857fe6a14459e1acb63f233d39bcfacdab8ebbbc60bca0f87843768e9c6a40ccea5e8ab6e095c3eb79d74c61d910e620eef33fe673a034494b74e50 + languageName: node + linkType: hard + +"@commitlint/message@npm:^21.2.0": + version: 21.2.0 + resolution: "@commitlint/message@npm:21.2.0" + checksum: 10c0/f3f97efd382970316aac689545b7f7bc278e114cce0540469ba56539f2ab78089b84bd0137c82be0908d1e251890e035348acd369c24c4011abf6445d8ec928f + languageName: node + linkType: hard + +"@commitlint/parse@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/parse@npm:21.2.3" + dependencies: + "@commitlint/types": "npm:^21.2.3" + conventional-changelog-angular: "npm:^9.0.0" + conventional-commits-parser: "npm:^7.0.0" + checksum: 10c0/5692b5298bf9668d45cde1a33087bf40c17ccf9c6421dbe314740b5a9779386261532386c4dfae421d82c80dbc1742ca5a78150dfeb6891421570b4206bb343b + languageName: node + linkType: hard + +"@commitlint/read@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/read@npm:21.2.3" + dependencies: + "@commitlint/top-level": "npm:^21.2.0" + "@commitlint/types": "npm:^21.2.3" + "@conventional-changelog/git-client": "npm:^3.0.0" + tinyexec: "npm:^1.0.0" + checksum: 10c0/7a0421c9135dbef584ecf8e4aa8d3eee2536cf575dab8dbcbf7ea8d79753fd31df5701cb877f413189763f1d2e0e6fbc7b6e2061b35b15acca1ed602a0b28ca3 + languageName: node + linkType: hard + +"@commitlint/resolve-extends@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/resolve-extends@npm:21.2.3" + dependencies: + "@commitlint/config-validator": "npm:^21.2.3" + "@commitlint/types": "npm:^21.2.3" + es-toolkit: "npm:^1.46.0" + global-directory: "npm:^5.0.0" + resolve-from: "npm:^5.0.0" + checksum: 10c0/527fb212ced6b5a1009f2a7ee972e58369196030d846d23f59a141c0f22e1b22fc19bfec0ff2739d63f72fa764d62dd78c0204d435f1134d62e0b88ea1defcd4 + languageName: node + linkType: hard + +"@commitlint/rules@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/rules@npm:21.2.3" + dependencies: + "@commitlint/ensure": "npm:^21.2.3" + "@commitlint/message": "npm:^21.2.0" + "@commitlint/to-lines": "npm:^21.0.1" + "@commitlint/types": "npm:^21.2.3" + checksum: 10c0/cb15fe727d0a0a8845b472eef1dc61885862ee23e1afd154e55a288a486fbc2a228ec87f0152a58a4d27646cc0a19eddee0855c15a3f528653b5c19512f0e115 + languageName: node + linkType: hard + +"@commitlint/to-lines@npm:^21.0.1": + version: 21.0.1 + resolution: "@commitlint/to-lines@npm:21.0.1" + checksum: 10c0/bdf0640e260e11aeb6c6450095477ff123d2792c19307aba11ffaf35170ec8f05244d106edfecfb979fe5c1559b821db77a9d844261ef443895cee1d847e15ca + languageName: node + linkType: hard + +"@commitlint/top-level@npm:^21.2.0": + version: 21.2.0 + resolution: "@commitlint/top-level@npm:21.2.0" + dependencies: + escalade: "npm:^3.2.0" + checksum: 10c0/2358c836db34a0405b8e7e4eda38ca120d80dafa0b6ca84b12460131649d7fbdad35e5b68de19db51cf3a4725ae8a721f8f1f6909dcd161d60e3953d96f775f3 + languageName: node + linkType: hard + +"@commitlint/types@npm:^21.2.3": + version: 21.2.3 + resolution: "@commitlint/types@npm:21.2.3" + dependencies: + conventional-commits-parser: "npm:^7.0.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/64a2dd651b6c54b0e332a77d5e39fac823cf5750f0f9d8f6f562b97143ee2b76b689537a2d2bf98e64258b5d951fb0e21bc83bcf7969ce4b084f8a588d933740 + languageName: node + linkType: hard + +"@conventional-changelog/git-client@npm:^3.0.0": + version: 3.1.2 + resolution: "@conventional-changelog/git-client@npm:3.1.2" + dependencies: + "@simple-libs/child-process-utils": "npm:^2.0.0" + "@simple-libs/stream-utils": "npm:^2.0.0" + semver: "npm:^7.5.2" + peerDependencies: + conventional-commits-filter: ^6.0.1 + conventional-commits-parser: ^7.1.2 + peerDependenciesMeta: + conventional-commits-filter: + optional: true + conventional-commits-parser: + optional: true + checksum: 10c0/ee07d87638e5a2ded3d14498fc9530f6023651573dd56ee729b587def16d13f94ae362c5b0c2a17acda538bad0787f7c4df5d36ee7164a92b9eb79cc0e039145 + languageName: node + linkType: hard + +"@conventional-changelog/template@npm:^1.4.0": + version: 1.4.0 + resolution: "@conventional-changelog/template@npm:1.4.0" + checksum: 10c0/052ac38b43f67b2572d101ced1dee2691a2ac3828cbc1041738cf1646b956ff0fb3e87258ec59f46eb2c3fcc9a68bb89f90b8dcb1e354880f2c71d8f5ef3a021 + languageName: node + linkType: hard + +"@drizzle-team/brocli@npm:^0.10.2": + version: 0.10.2 + resolution: "@drizzle-team/brocli@npm:0.10.2" + checksum: 10c0/3d8b99d680f0b14fea32b45c59b938b6665e0840cc67f04801b1aa3c6747da3c7d01c00e321645034fa100abdba7e0c20ce07cf46fc2ca769ee4cafd97562484 + languageName: node + linkType: hard + +"@emmetio/abbreviation@npm:^2.3.3": + version: 2.3.3 + resolution: "@emmetio/abbreviation@npm:2.3.3" + dependencies: + "@emmetio/scanner": "npm:^1.0.4" + checksum: 10c0/835b460706d5920a6f9a569a44b7d98e88d5530e3983af3678b44fa38b4cbdf68b5df933476d72e340779b16e7e7962ffa63142db8d2f59b1175a11c30c14635 + languageName: node + linkType: hard + +"@emmetio/css-abbreviation@npm:^2.1.8": + version: 2.1.8 + resolution: "@emmetio/css-abbreviation@npm:2.1.8" + dependencies: + "@emmetio/scanner": "npm:^1.0.4" + checksum: 10c0/b5b3b39e773185d848b634e48e1b520e6ebffd28bfd0ba34fbcf877ca77e0edb8c7bbf58230cb0621f80f579bd7fd0265f00ab5e09ac482a835897cbdb6182a6 + languageName: node + linkType: hard + +"@emmetio/css-parser@npm:^0.4.1": + version: 0.4.1 + resolution: "@emmetio/css-parser@npm:0.4.1" + dependencies: + "@emmetio/stream-reader": "npm:^2.2.0" + "@emmetio/stream-reader-utils": "npm:^0.1.0" + checksum: 10c0/d34bf6c8d431eaa2b8b9cdff14360755ca7e235392a23666fe89f2e2b14ac54657712c196bbb6cb016b5bbf3e19faf7f413f7de815d35a26fce82a507191950e + languageName: node + linkType: hard + +"@emmetio/html-matcher@npm:^1.3.0": + version: 1.3.0 + resolution: "@emmetio/html-matcher@npm:1.3.0" + dependencies: + "@emmetio/scanner": "npm:^1.0.0" + checksum: 10c0/354f6d4d52ffb124883cc350d6a09942f2ec352b03dfb928ba8c3078b0f570041b6095367367094a307388c158e6b053565c09a9c1db699517801435a0eadb5c + languageName: node + linkType: hard + +"@emmetio/scanner@npm:^1.0.0, @emmetio/scanner@npm:^1.0.4": + version: 1.0.4 + resolution: "@emmetio/scanner@npm:1.0.4" + checksum: 10c0/ae6244e563caaff0f88d7afefc33fd6cfb7cc767ce914b54d35b46002637948cfc65951dba6d6941328afa54c721c225836fafce2de40fb7643660ba09fe7372 + languageName: node + linkType: hard + +"@emmetio/stream-reader-utils@npm:^0.1.0": + version: 0.1.0 + resolution: "@emmetio/stream-reader-utils@npm:0.1.0" + checksum: 10c0/8630a066200976aa07ce25708484e5c2458182961280dc4637a7a6e98e1f3bb59fb7bf3bf26380f74c6ff1fd40d3dbf1143f21c721833199e45f669614a33b87 + languageName: node + linkType: hard + +"@emmetio/stream-reader@npm:^2.2.0": + version: 2.2.0 + resolution: "@emmetio/stream-reader@npm:2.2.0" + checksum: 10c0/d1a5c83b1ef062ce89b48e1fc170cd5f21b0dfa6132dc41421ef7eda455563227c047977cd4c7b70f072fa73b5af553b04cafe381c600df9bd17c58ca8238e8b + languageName: node + linkType: hard + +"@emnapi/core@npm:1.11.1": + version: 1.11.1 + resolution: "@emnapi/core@npm:1.11.1" + dependencies: + "@emnapi/wasi-threads": "npm:1.2.2" + tslib: "npm:^2.4.0" + checksum: 10c0/2c6defdac2d1d26090384655d7d6c9614fa553853b1760597686749e9375dc2aa0dae80a2615b81c254600f5d531d07d8466cde0d331a8caae64b93f3ca5937e + languageName: node + linkType: hard + +"@emnapi/core@npm:1.11.3, @emnapi/core@npm:^1.11.1": + version: 1.11.3 + resolution: "@emnapi/core@npm:1.11.3" + dependencies: + "@emnapi/wasi-threads": "npm:1.2.3" + tslib: "npm:^2.4.0" + checksum: 10c0/4ca08d349a82d5d2887ccc9e12df630877b0412ddcd59b9faee61e3c3947ccead27a18257a18bfe17abdf2b0709857808ad75d423ac49edd50c32fb140a7ed6e + languageName: node + linkType: hard + +"@emnapi/runtime@npm:1.11.1": + version: 1.11.1 + resolution: "@emnapi/runtime@npm:1.11.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/04332fb62076afc440aa23316c04bec42f584ca8b074e5507d08e2b33a47cbe0493b1aadb8f3c1057b64ae1e17f5bde1a7bc37f7facc9d0bc25c18197cbd366f + languageName: node + linkType: hard + +"@emnapi/runtime@npm:^1.11.1, @emnapi/runtime@npm:^1.11.3": + version: 1.11.3 + resolution: "@emnapi/runtime@npm:1.11.3" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/a00f1020fefb9d4145c367f93a9fddb383a00da8ffd7871e20b19659890379b83aeecb9f84d7d0eda5456343f4a09eb05b0acb5153b0d3d889539dedb1ed87c3 + languageName: node + linkType: hard + +"@emnapi/wasi-threads@npm:1.2.2": + version: 1.2.2 + resolution: "@emnapi/wasi-threads@npm:1.2.2" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/f0dc8269d6b20ae5a7c7b36e7a6a333452009d461038ef4febb29da2f3f78c1e2b1576d7e8970a5c5789ed3caedc1f80f5b0c2a5373bdaf8d03b20432bb55747 + languageName: node + linkType: hard + +"@emnapi/wasi-threads@npm:1.2.3, @emnapi/wasi-threads@npm:^1.2.2": + version: 1.2.3 + resolution: "@emnapi/wasi-threads@npm:1.2.3" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/5aed84bc5dbe867973b20406ca1ed95661a4892ecaef80378fdf2d37424b3f7b9c428df8a3e1d82b783a3345a530032bf049e699e1f113ea52203c3e9b4e6ce5 + languageName: node + linkType: hard + +"@esbuild-kit/core-utils@npm:^3.3.2": + version: 3.3.2 + resolution: "@esbuild-kit/core-utils@npm:3.3.2" + dependencies: + esbuild: "npm:~0.18.20" + source-map-support: "npm:^0.5.21" + checksum: 10c0/d856f5bd720814593f911d781ed7558a3f8ec1a39802f3831d0eea0d1306e0e2dc11b7b2443af621c413ec6557f1f3034a9a4f1472a4cb40e52cd6e3b356aa05 + languageName: node + linkType: hard + +"@esbuild-kit/esm-loader@npm:^2.5.5": + version: 2.6.5 + resolution: "@esbuild-kit/esm-loader@npm:2.6.5" + dependencies: + "@esbuild-kit/core-utils": "npm:^3.3.2" + get-tsconfig: "npm:^4.7.0" + checksum: 10c0/6894b29176eda62bdce0d458d57f32daed5cb8fcff14cb3ddfbc995cfe3e2fa8599f3b0b1af66db446903b30167f57069f27e9cf79a69cf9b41f557115811cde + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/aix-ppc64@npm:0.19.12" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/aix-ppc64@npm:0.25.12" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/aix-ppc64@npm:0.28.2" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm64@npm:0.18.20" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-arm64@npm:0.19.12" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm64@npm:0.25.12" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/android-arm64@npm:0.28.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm@npm:0.18.20" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-arm@npm:0.19.12" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm@npm:0.25.12" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/android-arm@npm:0.28.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-x64@npm:0.18.20" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-x64@npm:0.19.12" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-x64@npm:0.25.12" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/android-x64@npm:0.28.2" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-arm64@npm:0.18.20" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/darwin-arm64@npm:0.19.12" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-arm64@npm:0.25.12" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/darwin-arm64@npm:0.28.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-x64@npm:0.18.20" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/darwin-x64@npm:0.19.12" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-x64@npm:0.25.12" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/darwin-x64@npm:0.28.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-arm64@npm:0.18.20" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/freebsd-arm64@npm:0.19.12" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-arm64@npm:0.25.12" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/freebsd-arm64@npm:0.28.2" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-x64@npm:0.18.20" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/freebsd-x64@npm:0.19.12" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-x64@npm:0.25.12" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/freebsd-x64@npm:0.28.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm64@npm:0.18.20" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-arm64@npm:0.19.12" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm64@npm:0.25.12" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-arm64@npm:0.28.2" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm@npm:0.18.20" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-arm@npm:0.19.12" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm@npm:0.25.12" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-arm@npm:0.28.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ia32@npm:0.18.20" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-ia32@npm:0.19.12" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ia32@npm:0.25.12" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-ia32@npm:0.28.2" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-loong64@npm:0.18.20" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-loong64@npm:0.19.12" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-loong64@npm:0.25.12" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-loong64@npm:0.28.2" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-mips64el@npm:0.18.20" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-mips64el@npm:0.19.12" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-mips64el@npm:0.25.12" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-mips64el@npm:0.28.2" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ppc64@npm:0.18.20" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-ppc64@npm:0.19.12" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ppc64@npm:0.25.12" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-ppc64@npm:0.28.2" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-riscv64@npm:0.18.20" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-riscv64@npm:0.19.12" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-riscv64@npm:0.25.12" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-riscv64@npm:0.28.2" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-s390x@npm:0.18.20" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-s390x@npm:0.19.12" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-s390x@npm:0.25.12" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-s390x@npm:0.28.2" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-x64@npm:0.18.20" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-x64@npm:0.19.12" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-x64@npm:0.25.12" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-x64@npm:0.28.2" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-arm64@npm:0.25.12" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/netbsd-arm64@npm:0.28.2" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/netbsd-x64@npm:0.18.20" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/netbsd-x64@npm:0.19.12" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-x64@npm:0.25.12" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/netbsd-x64@npm:0.28.2" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-arm64@npm:0.25.12" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/openbsd-arm64@npm:0.28.2" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/openbsd-x64@npm:0.18.20" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/openbsd-x64@npm:0.19.12" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-x64@npm:0.25.12" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/openbsd-x64@npm:0.28.2" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openharmony-arm64@npm:0.25.12" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/openharmony-arm64@npm:0.28.2" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/sunos-x64@npm:0.18.20" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/sunos-x64@npm:0.19.12" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/sunos-x64@npm:0.25.12" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/sunos-x64@npm:0.28.2" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-arm64@npm:0.18.20" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-arm64@npm:0.19.12" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-arm64@npm:0.25.12" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/win32-arm64@npm:0.28.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-ia32@npm:0.18.20" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-ia32@npm:0.19.12" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-ia32@npm:0.25.12" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/win32-ia32@npm:0.28.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-x64@npm:0.18.20" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-x64@npm:0.19.12" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-x64@npm:0.25.12" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/win32-x64@npm:0.28.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@floating-ui/core@npm:^1.8.0": + version: 1.8.0 + resolution: "@floating-ui/core@npm:1.8.0" + dependencies: + "@floating-ui/utils": "npm:^0.2.12" + checksum: 10c0/cece958f6fab0f8cd7740cd57988c4f8bc5356dca39dd4c093433d44c91952c3a02c7a54d93d7ed6c078b6544d531a29f66621d2839c8bfbb4467dca6aed7df0 + languageName: node + linkType: hard + +"@floating-ui/dom@npm:^1.8.0": + version: 1.8.0 + resolution: "@floating-ui/dom@npm:1.8.0" + dependencies: + "@floating-ui/core": "npm:^1.8.0" + "@floating-ui/utils": "npm:^0.2.12" + checksum: 10c0/c32b2de7a596b69cfffa909c7028825e14f6319be1b45da529f8943c89fe16b75a49c214e800c87ded3f172353d3b99163d50072f11410488e756961b7b629cc + languageName: node + linkType: hard + +"@floating-ui/react-dom@npm:^2.0.0": + version: 2.1.9 + resolution: "@floating-ui/react-dom@npm:2.1.9" + dependencies: + "@floating-ui/dom": "npm:^1.8.0" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10c0/9fa9851069738cb9591a1ab2da16e257f193eae5fd2d6d48ad0b1e73f3c049929b6805c5ad43d4af3c1da63c60b215b0ee4651a1fe5caeb80e66d9d423744348 + languageName: node + linkType: hard + +"@floating-ui/utils@npm:^0.2.12": + version: 0.2.12 + resolution: "@floating-ui/utils@npm:0.2.12" + checksum: 10c0/bb910b90d56991a62011afaf5f8e0c4b7fb6dab69403f4dd17fbd6eb25560b28d533dff9791f270b4f459852e9006a1077b5d73cbedb5e34d9424afc6a828314 + languageName: node + linkType: hard + +"@img/colour@npm:^1.1.0": + version: 1.1.0 + resolution: "@img/colour@npm:1.1.0" + checksum: 10c0/2ebea2c0bbaee73b99badcefa04e1e71d83f36e5369337d3121dca841f4569533c4e2faddda6d62dd247f0d5cca143711f9446c59bcce81e427ba433a7a94a17 + languageName: node + linkType: hard + +"@img/sharp-darwin-arm64@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-darwin-arm64@npm:0.35.4" + dependencies: + "@img/sharp-libvips-darwin-arm64": "npm:1.3.3" + dependenciesMeta: + "@img/sharp-libvips-darwin-arm64": + optional: true + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-darwin-x64@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-darwin-x64@npm:0.35.4" + dependencies: + "@img/sharp-libvips-darwin-x64": "npm:1.3.3" + dependenciesMeta: + "@img/sharp-libvips-darwin-x64": + optional: true + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-freebsd-wasm32@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-freebsd-wasm32@npm:0.35.4" + dependencies: + "@img/sharp-wasm32": "npm:0.35.4" + conditions: os=freebsd + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-arm64@npm:1.3.3": + version: 1.3.3 + resolution: "@img/sharp-libvips-darwin-arm64@npm:1.3.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-x64@npm:1.3.3": + version: 1.3.3 + resolution: "@img/sharp-libvips-darwin-x64@npm:1.3.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm64@npm:1.3.3": + version: 1.3.3 + resolution: "@img/sharp-libvips-linux-arm64@npm:1.3.3" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm@npm:1.3.3": + version: 1.3.3 + resolution: "@img/sharp-libvips-linux-arm@npm:1.3.3" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-ppc64@npm:1.3.3": + version: 1.3.3 + resolution: "@img/sharp-libvips-linux-ppc64@npm:1.3.3" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-riscv64@npm:1.3.3": + version: 1.3.3 + resolution: "@img/sharp-libvips-linux-riscv64@npm:1.3.3" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-s390x@npm:1.3.3": + version: 1.3.3 + resolution: "@img/sharp-libvips-linux-s390x@npm:1.3.3" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-x64@npm:1.3.3": + version: 1.3.3 + resolution: "@img/sharp-libvips-linux-x64@npm:1.3.3" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-arm64@npm:1.3.3": + version: 1.3.3 + resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.3.3" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-x64@npm:1.3.3": + version: 1.3.3 + resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.3.3" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linux-arm64@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-linux-arm64@npm:0.35.4" + dependencies: + "@img/sharp-libvips-linux-arm64": "npm:1.3.3" + dependenciesMeta: + "@img/sharp-libvips-linux-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-arm@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-linux-arm@npm:0.35.4" + dependencies: + "@img/sharp-libvips-linux-arm": "npm:1.3.3" + dependenciesMeta: + "@img/sharp-libvips-linux-arm": + optional: true + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-ppc64@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-linux-ppc64@npm:0.35.4" + dependencies: + "@img/sharp-libvips-linux-ppc64": "npm:1.3.3" + dependenciesMeta: + "@img/sharp-libvips-linux-ppc64": + optional: true + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-riscv64@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-linux-riscv64@npm:0.35.4" + dependencies: + "@img/sharp-libvips-linux-riscv64": "npm:1.3.3" + dependenciesMeta: + "@img/sharp-libvips-linux-riscv64": + optional: true + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-s390x@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-linux-s390x@npm:0.35.4" + dependencies: + "@img/sharp-libvips-linux-s390x": "npm:1.3.3" + dependenciesMeta: + "@img/sharp-libvips-linux-s390x": + optional: true + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-x64@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-linux-x64@npm:0.35.4" + dependencies: + "@img/sharp-libvips-linux-x64": "npm:1.3.3" + dependenciesMeta: + "@img/sharp-libvips-linux-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-arm64@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-linuxmusl-arm64@npm:0.35.4" + dependencies: + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.3.3" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-x64@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-linuxmusl-x64@npm:0.35.4" + dependencies: + "@img/sharp-libvips-linuxmusl-x64": "npm:1.3.3" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-wasm32@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-wasm32@npm:0.35.4" + dependencies: + "@emnapi/runtime": "npm:^1.11.3" + checksum: 10c0/7f5e394f168eb2f038b8ae4949fb5434085d1db931ede2ae1389479d7a1dbe11e2fc9d58e8e9830bff744ba996256c9e411be1d466dfcbf6e1f4d153b84f25ff + languageName: node + linkType: hard + +"@img/sharp-webcontainers-wasm32@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-webcontainers-wasm32@npm:0.35.4" + dependencies: + "@img/sharp-wasm32": "npm:0.35.4" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@img/sharp-win32-arm64@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-win32-arm64@npm:0.35.4" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-win32-ia32@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-win32-ia32@npm:0.35.4" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@img/sharp-win32-x64@npm:0.35.4": + version: 0.35.4 + resolution: "@img/sharp-win32-x64@npm:0.35.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e + languageName: node + linkType: hard + +"@isaacs/fs-minipass@npm:^4.0.0": + version: 4.0.1 + resolution: "@isaacs/fs-minipass@npm:4.0.1" + dependencies: + minipass: "npm:^7.0.4" + checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2 + languageName: node + linkType: hard + +"@joshwooding/vite-plugin-react-docgen-typescript@npm:0.6.1": + version: 0.6.1 + resolution: "@joshwooding/vite-plugin-react-docgen-typescript@npm:0.6.1" + dependencies: + glob: "npm:^10.0.0" + magic-string: "npm:^0.30.0" + react-docgen-typescript: "npm:^2.2.2" + peerDependencies: + typescript: ">= 4.3.x" + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/0bcc2adbb49158018102bd9d84cd8572c770daee3d46733157933ef0330953bd5b9e102c26f2338ee7dfb8f21a7bb937134d23f8a7935d5dc88525a253557467 + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.12, @jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.13 + resolution: "@jridgewell/gen-mapping@npm:0.3.13" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/9a7d65fb13bd9aec1fbab74cda08496839b7e2ceb31f5ab922b323e94d7c481ce0fc4fd7e12e2610915ed8af51178bdc61e168e92a8c8b8303b030b03489b13b + languageName: node + linkType: hard + +"@jridgewell/remapping@npm:^2.3.5": + version: 2.3.5 + resolution: "@jridgewell/remapping@npm:2.3.5" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/3de494219ffeb2c5c38711d0d7bb128097edf91893090a2dbc8ee0b55d092bb7347b1fd0f478486c5eab010e855c73927b1666f2107516d472d24a73017d1194 + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0, @jridgewell/sourcemap-codec@npm:^1.5.5, @jridgewell/sourcemap-codec@npm:^1.6.0": + version: 1.6.0 + resolution: "@jridgewell/sourcemap-codec@npm:1.6.0" + checksum: 10c0/b5be700e45a775f218589c3466c4ffea630582b4988657652da464e5ab8a7d18bf928ee4fd2363fb346ede4bea9c6ff0bae05a538358c4565ece6199531b5f72 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.28": + version: 0.3.31 + resolution: "@jridgewell/trace-mapping@npm:0.3.31" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/4b30ec8cd56c5fd9a661f088230af01e0c1a3888d11ffb6b47639700f71225be21d1f7e168048d6d4f9449207b978a235c07c8f15c07705685d16dc06280e9d9 + languageName: node + linkType: hard + +"@libsql/client@npm:^0.14.0": + version: 0.14.0 + resolution: "@libsql/client@npm:0.14.0" + dependencies: + "@libsql/core": "npm:^0.14.0" + "@libsql/hrana-client": "npm:^0.7.0" + js-base64: "npm:^3.7.5" + libsql: "npm:^0.4.4" + promise-limit: "npm:^2.7.0" + checksum: 10c0/9c6bab468453df765f647422c772af3578f1e108b663a80b99063f47ed3542db26ae0fcdba2e153d72e6d5089c5caeba947a167a6c065b0191a0832621539335 + languageName: node + linkType: hard + +"@libsql/core@npm:^0.14.0": + version: 0.14.0 + resolution: "@libsql/core@npm:0.14.0" + dependencies: + js-base64: "npm:^3.7.5" + checksum: 10c0/327bb991cf191d5a9a9fc0cc1a17123f7ca88f222187a3bde845fbad8ceaeaa1f139882080e4b2969da57b83e576c52702572e2838d1743c6bff75f95e6f774a + languageName: node + linkType: hard + +"@libsql/darwin-arm64@npm:0.4.7": + version: 0.4.7 + resolution: "@libsql/darwin-arm64@npm:0.4.7" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@libsql/darwin-x64@npm:0.4.7": + version: 0.4.7 + resolution: "@libsql/darwin-x64@npm:0.4.7" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@libsql/hrana-client@npm:^0.7.0": + version: 0.7.0 + resolution: "@libsql/hrana-client@npm:0.7.0" + dependencies: + "@libsql/isomorphic-fetch": "npm:^0.3.1" + "@libsql/isomorphic-ws": "npm:^0.1.5" + js-base64: "npm:^3.7.5" + node-fetch: "npm:^3.3.2" + checksum: 10c0/85bbe3074e3efcc56fbc33a741afd06c1211f5d7772ea46155ce6fe6578e31ed517c7017510c5bfea3dc28031e95c0e284096154f28cbbab06751fd7d9437e28 + languageName: node + linkType: hard + +"@libsql/isomorphic-fetch@npm:^0.3.1": + version: 0.3.1 + resolution: "@libsql/isomorphic-fetch@npm:0.3.1" + checksum: 10c0/f415f23f0c9921d6aae5133f91194d85a70e555b6c931fb4ff272af94335a46f5893433fbbb8fa24ee45bab8acce8ba5f060a22567ff3fb0577883bfcd1dec9e + languageName: node + linkType: hard + +"@libsql/isomorphic-ws@npm:^0.1.5": + version: 0.1.5 + resolution: "@libsql/isomorphic-ws@npm:0.1.5" + dependencies: + "@types/ws": "npm:^8.5.4" + ws: "npm:^8.13.0" + checksum: 10c0/7028bbc50dd094cdcbe56714dbf52fb646812d1b042c1973e61293f4a1cb5b81d5af670530a2463a2ba485f84f7728daf3eb75d40a7f55316ee4f7015dcc99ae + languageName: node + linkType: hard + +"@libsql/linux-arm64-gnu@npm:0.4.7": + version: 0.4.7 + resolution: "@libsql/linux-arm64-gnu@npm:0.4.7" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@libsql/linux-arm64-musl@npm:0.4.7": + version: 0.4.7 + resolution: "@libsql/linux-arm64-musl@npm:0.4.7" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@libsql/linux-x64-gnu@npm:0.4.7": + version: 0.4.7 + resolution: "@libsql/linux-x64-gnu@npm:0.4.7" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@libsql/linux-x64-musl@npm:0.4.7": + version: 0.4.7 + resolution: "@libsql/linux-x64-musl@npm:0.4.7" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@libsql/win32-x64-msvc@npm:0.4.7": + version: 0.4.7 + resolution: "@libsql/win32-x64-msvc@npm:0.4.7" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@mdx-js/react@npm:^3.0.0": + version: 3.1.1 + resolution: "@mdx-js/react@npm:3.1.1" + dependencies: + "@types/mdx": "npm:^2.0.0" + peerDependencies: + "@types/react": ">=16" + react: ">=16" + checksum: 10c0/34ca98bc2a0f969894ea144dc5c8a5294690505458cd24965cd9be854d779c193ad9192bf9143c4c18438fafd1902e100d99067e045c69319288562d497558c6 + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-x64-gnu@npm:1.5.1": + version: 1.5.1 + resolution: "@napi-rs/lzma-linux-x64-gnu@npm:1.5.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/wasm-runtime@npm:1.2.4, @napi-rs/wasm-runtime@npm:^1.1.4, @napi-rs/wasm-runtime@npm:^1.2.3, @napi-rs/wasm-runtime@npm:^1.2.4": + version: 1.2.4 + resolution: "@napi-rs/wasm-runtime@npm:1.2.4" + dependencies: + "@tybys/wasm-util": "npm:^0.10.3" + peerDependencies: + "@emnapi/core": ^1.7.1 || ^2.0.0-alpha.4 + "@emnapi/runtime": ^1.7.1 || ^2.0.0-alpha.4 + checksum: 10c0/4bd7be975f7c806bf7c33255b53257cdcd603c573e453b2f6878550bcfd59722d0d34c97627ee6a0353d91274b738d29b32ba1f0fe552a5fca1c2da80aa17bdc + languageName: node + linkType: hard + +"@neon-rs/load@npm:^0.0.4": + version: 0.0.4 + resolution: "@neon-rs/load@npm:0.0.4" + checksum: 10c0/546fa4e48aa9cdb402f0a3524b591b1cac863bcfdd0217432323dba42ad37ece24b736019e6196e34326201db6b6deb410d7a983ac3c54f322619c9b6bd568bb + languageName: node + linkType: hard + +"@oslojs/encoding@npm:^1.1.0": + version: 1.1.0 + resolution: "@oslojs/encoding@npm:1.1.0" + checksum: 10c0/5553a0974dca60e1a8b247b7b97abcb141cc7ee4e22444f424a07921d6a5f76a43c316f3ee669222787fdef6549f8749cc6d68ff5a631e2542521c56fe36417f + languageName: node + linkType: hard + +"@oxc-project/types@npm:=0.150.0": + version: 0.150.0 + resolution: "@oxc-project/types@npm:0.150.0" + checksum: 10c0/e8b72ce72f5cd56b6c1da67dd65677c54850354acb0d2493016c791088f5cad991ba14ea79948ed766f62f6b6048e5de8ace163e154c8f21c550753052ccfe42 + languageName: node + linkType: hard + +"@petamoriken/float16@npm:^3.8.7": + version: 3.9.3 + resolution: "@petamoriken/float16@npm:3.9.3" + checksum: 10c0/de0a1a0224862cba16e6b614e6a5dd557c545c3f0372db21f61d7bcce7595fa63a28d10512c908acfbe177d209a39cea406f190159936f5891b3c095d460fdc2 + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd + languageName: node + linkType: hard + +"@radix-ui/primitive@npm:1.1.7": + version: 1.1.7 + resolution: "@radix-ui/primitive@npm:1.1.7" + checksum: 10c0/6b1f8b964a57ece3490582d60df79a1c80fdba4c4331b806c4e04b11f42ac46d40a927bfc692dbd881136ec38c3f060c525dbacd6182ba6f3f0bd4284db1028e + languageName: node + linkType: hard + +"@radix-ui/react-arrow@npm:1.1.15": + version: 1.1.15 + resolution: "@radix-ui/react-arrow@npm:1.1.15" + dependencies: + "@radix-ui/react-primitive": "npm:2.1.10" + 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 + checksum: 10c0/06fa0a29ae332617d7912e4f25e952d110905264362e7266d2aa423e81aff492d0f1eaa7dd834b3271e6f5d45f56c8c54585d10d771356fb789e50c959f555ca + languageName: node + linkType: hard + +"@radix-ui/react-avatar@npm:^1.2.6": + version: 1.2.6 + resolution: "@radix-ui/react-avatar@npm:1.2.6" + dependencies: + "@radix-ui/primitive": "npm:1.1.7" + "@radix-ui/react-context": "npm:1.2.2" + "@radix-ui/react-primitive": "npm:2.1.10" + "@radix-ui/react-use-callback-ref": "npm:1.1.4" + "@radix-ui/react-use-is-hydrated": "npm:0.1.3" + "@radix-ui/react-use-layout-effect": "npm:1.1.4" + 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 + checksum: 10c0/e5397f3fe042a1440b19076d2dd0131c9f5e42cce6d892151225644bb794af958f508fc49005a85603bd3e55bc9cf78fe7c19685c02a1961646bf415623ab3f6 + languageName: node + linkType: hard + +"@radix-ui/react-collection@npm:1.1.15": + version: 1.1.15 + resolution: "@radix-ui/react-collection@npm:1.1.15" + dependencies: + "@radix-ui/react-compose-refs": "npm:1.1.5" + "@radix-ui/react-context": "npm:1.2.2" + "@radix-ui/react-primitive": "npm:2.1.10" + "@radix-ui/react-slot": "npm:1.3.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 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/9f9d3f6290026fd383503ebc260d698d59287016e90a3bd5aa4a9696b8cab733b207e5558ddffa3bb877c68f8658c4a3a52fb9314c7cbc83a4b6349808c5bf4c + languageName: node + linkType: hard + +"@radix-ui/react-compose-refs@npm:1.1.5": + version: 1.1.5 + resolution: "@radix-ui/react-compose-refs@npm:1.1.5" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/ab5cc8cc2f5be285981cffb64aa956d3a28427c649a953d125e900e1befb8b9a55f2d7a31bc01059d29df43ff4ae14ebc6cca7e4c2084d638cef5e5aef7cc263 + languageName: node + linkType: hard + +"@radix-ui/react-context@npm:1.2.2": + version: 1.2.2 + resolution: "@radix-ui/react-context@npm:1.2.2" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/f5c15339558d64901c99f243a2538be9b28eb20e0dae1e44c8872ed12580ec27f5f309fd491f85f1b0dcb214c90fdae7eede7fe2b0d26569c4dbeffbeff451c0 + languageName: node + linkType: hard + +"@radix-ui/react-direction@npm:1.1.4": + version: 1.1.4 + resolution: "@radix-ui/react-direction@npm:1.1.4" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/505e6e45b53f2b6a98c82895e1fe0b04c048bf70dbcabe2e3a4bbc05e3cf4759a5e7d9865360eac78da5c56ed3fdd82efce1511179f397525d14de614181de61 + languageName: node + linkType: hard + +"@radix-ui/react-dismissable-layer@npm:1.1.19": + version: 1.1.19 + resolution: "@radix-ui/react-dismissable-layer@npm:1.1.19" + dependencies: + "@radix-ui/primitive": "npm:1.1.7" + "@radix-ui/react-compose-refs": "npm:1.1.5" + "@radix-ui/react-primitive": "npm:2.1.10" + "@radix-ui/react-use-callback-ref": "npm:1.1.4" + "@radix-ui/react-use-effect-event": "npm:0.0.5" + 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 + checksum: 10c0/205f2fded4a9e303fcc0d67b78274db84d37d0d35177bb99602c72202a6dcadc39f4cbf936a09e1303b4b42a2b494a279ead9e626cb1a41015152a91ad069fc5 + languageName: node + linkType: hard + +"@radix-ui/react-dropdown-menu@npm:^2.1.24": + version: 2.1.24 + resolution: "@radix-ui/react-dropdown-menu@npm:2.1.24" + dependencies: + "@radix-ui/primitive": "npm:1.1.7" + "@radix-ui/react-compose-refs": "npm:1.1.5" + "@radix-ui/react-context": "npm:1.2.2" + "@radix-ui/react-id": "npm:1.1.4" + "@radix-ui/react-menu": "npm:2.1.24" + "@radix-ui/react-primitive": "npm:2.1.10" + "@radix-ui/react-use-controllable-state": "npm:1.2.6" + 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 + checksum: 10c0/bdf10d0f1c3cee34a661a33b0f6effaec26586ed2ebd8fd2214e38c5ba226579c86bd86e68af5a2bd750ab3f09b9e5efb908063c0f4cb0fc228343c0159179d2 + languageName: node + linkType: hard + +"@radix-ui/react-focus-guards@npm:1.1.6": + version: 1.1.6 + resolution: "@radix-ui/react-focus-guards@npm:1.1.6" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/5579dfe369f254183a010964e6df8b74c7b36e50189aef18c92538d0196785d68869e3170af77005276f393cd112b7711d5c7cb9e7ec2ca1e0b4794157447978 + languageName: node + linkType: hard + +"@radix-ui/react-focus-scope@npm:1.1.16": + version: 1.1.16 + resolution: "@radix-ui/react-focus-scope@npm:1.1.16" + dependencies: + "@radix-ui/react-compose-refs": "npm:1.1.5" + "@radix-ui/react-primitive": "npm:2.1.10" + "@radix-ui/react-use-callback-ref": "npm:1.1.4" + 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 + checksum: 10c0/2c86dafac81a73295f81834ff85c4af4456671f7d26631cb800837b284318d181c5de043cac2e17bedd6e8a009e3de907ef9435ea9ec5a94518cbaf656368550 + languageName: node + linkType: hard + +"@radix-ui/react-id@npm:1.1.4": + version: 1.1.4 + resolution: "@radix-ui/react-id@npm:1.1.4" + dependencies: + "@radix-ui/react-use-layout-effect": "npm:1.1.4" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/7afa00d12d7f52b5067034108eb7d5551928f9d52875589f858ea1d5117cac3684f8ea291d4f09a88b9499bc4bb6e3279e4a9073d7c8feb15a855d28656b53a4 + languageName: node + linkType: hard + +"@radix-ui/react-label@npm:^2.1.15": + version: 2.1.15 + resolution: "@radix-ui/react-label@npm:2.1.15" + dependencies: + "@radix-ui/react-primitive": "npm:2.1.10" + 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 + checksum: 10c0/e1ef6d359488460c2a5e16b0f6513260553dca497db4f5b7d287e5fa38b3623335ff30ede278f5c1f0187fefaa571ecaf45c23f9b26ffefb262e96592f14783b + languageName: node + linkType: hard + +"@radix-ui/react-menu@npm:2.1.24": + version: 2.1.24 + resolution: "@radix-ui/react-menu@npm:2.1.24" + dependencies: + "@radix-ui/primitive": "npm:1.1.7" + "@radix-ui/react-collection": "npm:1.1.15" + "@radix-ui/react-compose-refs": "npm:1.1.5" + "@radix-ui/react-context": "npm:1.2.2" + "@radix-ui/react-direction": "npm:1.1.4" + "@radix-ui/react-dismissable-layer": "npm:1.1.19" + "@radix-ui/react-focus-guards": "npm:1.1.6" + "@radix-ui/react-focus-scope": "npm:1.1.16" + "@radix-ui/react-id": "npm:1.1.4" + "@radix-ui/react-popper": "npm:1.3.7" + "@radix-ui/react-portal": "npm:1.1.17" + "@radix-ui/react-presence": "npm:1.1.10" + "@radix-ui/react-primitive": "npm:2.1.10" + "@radix-ui/react-roving-focus": "npm:1.1.19" + "@radix-ui/react-slot": "npm:1.3.3" + "@radix-ui/react-use-callback-ref": "npm:1.1.4" + aria-hidden: "npm:^1.2.4" + react-remove-scroll: "npm:^2.7.2" + 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 + checksum: 10c0/01fd48bf24c4a43e799a9f0d4b4dc5b2bde57cd56307d0bb179c97dab99b902f5edb8f48899c21b623bcdfbc80d59e8a9ca5670b86c40f0b8ca4b90712bc3bcc + languageName: node + linkType: hard + +"@radix-ui/react-popper@npm:1.3.7": + version: 1.3.7 + resolution: "@radix-ui/react-popper@npm:1.3.7" + dependencies: + "@floating-ui/react-dom": "npm:^2.0.0" + "@radix-ui/react-arrow": "npm:1.1.15" + "@radix-ui/react-compose-refs": "npm:1.1.5" + "@radix-ui/react-context": "npm:1.2.2" + "@radix-ui/react-primitive": "npm:2.1.10" + "@radix-ui/react-use-callback-ref": "npm:1.1.4" + "@radix-ui/react-use-layout-effect": "npm:1.1.4" + "@radix-ui/react-use-rect": "npm:1.1.4" + "@radix-ui/react-use-size": "npm:1.1.4" + "@radix-ui/rect": "npm:1.1.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 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/f32e85f70d2161d724df4adae4103d5c1f61ff344d25ad51c4f3d0a02b6251890b864dde3313093d748efe824dc9cceb40a068ac68b10bd1163d4b678e01d6e3 + languageName: node + linkType: hard + +"@radix-ui/react-portal@npm:1.1.17": + version: 1.1.17 + resolution: "@radix-ui/react-portal@npm:1.1.17" + dependencies: + "@radix-ui/react-primitive": "npm:2.1.10" + "@radix-ui/react-use-layout-effect": "npm:1.1.4" + 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 + checksum: 10c0/f3358ef906582ee8dd8ae62db1f1c1e9810e0554ff8e1a887c731fb35d3b673791e9d338cab884f3eddd74e24afd8e21679b1cc24fd035c68ad71dd09ed71bae + languageName: node + linkType: hard + +"@radix-ui/react-presence@npm:1.1.10": + version: 1.1.10 + resolution: "@radix-ui/react-presence@npm:1.1.10" + dependencies: + "@radix-ui/react-use-layout-effect": "npm:1.1.4" + 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 + checksum: 10c0/5ef8048ea02ecfd11284e229efa33f75074189788b68edb9fe38140cbc56cc40ee77f017f34e3c3d3a5dbc1fb12b5a18d5a9ef2a84ef966a1d90502be5a84617 + languageName: node + linkType: hard + +"@radix-ui/react-primitive@npm:2.1.10": + version: 2.1.10 + resolution: "@radix-ui/react-primitive@npm:2.1.10" + dependencies: + "@radix-ui/react-slot": "npm:1.3.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 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/3be05e842d20a5eb08cebbc1d4d08f24a4c09af8cb864e1d1dddf27f362b739ce0edc1c9fc188a7c734cecd8f5df21e379c15774230f4ab11ce2a8229d3598da + languageName: node + linkType: hard + +"@radix-ui/react-roving-focus@npm:1.1.19": + version: 1.1.19 + resolution: "@radix-ui/react-roving-focus@npm:1.1.19" + dependencies: + "@radix-ui/primitive": "npm:1.1.7" + "@radix-ui/react-collection": "npm:1.1.15" + "@radix-ui/react-compose-refs": "npm:1.1.5" + "@radix-ui/react-context": "npm:1.2.2" + "@radix-ui/react-direction": "npm:1.1.4" + "@radix-ui/react-id": "npm:1.1.4" + "@radix-ui/react-primitive": "npm:2.1.10" + "@radix-ui/react-use-callback-ref": "npm:1.1.4" + "@radix-ui/react-use-controllable-state": "npm:1.2.6" + "@radix-ui/react-use-is-hydrated": "npm:0.1.3" + "@radix-ui/react-use-layout-effect": "npm:1.1.4" + 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 + checksum: 10c0/b11083f923635d4cddd5a45c5145e8684ac8311b7a9e3970cb60a0a4f5c1ab692b502a732f02f716560e1308c2d5e9b6925fe8a9479d7aadfe0f6156809eeafb + languageName: node + linkType: hard + +"@radix-ui/react-separator@npm:^1.1.15": + version: 1.1.15 + resolution: "@radix-ui/react-separator@npm:1.1.15" + dependencies: + "@radix-ui/react-primitive": "npm:2.1.10" + 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 + checksum: 10c0/72c9142ad39ca620985a98eb7bc1e2a4edc7d75246003ff320e29855919b84de68c3705d68f29bb5fc51f616ddaa9265360dec7b972359d6c2422dd719bde818 + languageName: node + linkType: hard + +"@radix-ui/react-slot@npm:1.3.3": + version: 1.3.3 + resolution: "@radix-ui/react-slot@npm:1.3.3" + dependencies: + "@radix-ui/react-compose-refs": "npm:1.1.5" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/7ed356819bbb40415ccd16bc8a8da3162966e827ea8fa404c2ae547518777aa193b0aad07f139f3c9d3280f51433e1cc4fa4cfd8dc7e8e6215e7b0d7a25e4b39 + languageName: node + linkType: hard + +"@radix-ui/react-use-callback-ref@npm:1.1.4": + version: 1.1.4 + resolution: "@radix-ui/react-use-callback-ref@npm:1.1.4" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/d5cc1248ab6a58b12eefd3115c98fb438b74b2e6dec463d2e097d27df8e9f40b9d8693ee10a422da4f7a688fbe52c62cc2fe3aa5972020b9425b86a2a28f5f01 + languageName: node + linkType: hard + +"@radix-ui/react-use-controllable-state@npm:1.2.6": + version: 1.2.6 + resolution: "@radix-ui/react-use-controllable-state@npm:1.2.6" + dependencies: + "@radix-ui/primitive": "npm:1.1.7" + "@radix-ui/react-use-effect-event": "npm:0.0.5" + "@radix-ui/react-use-layout-effect": "npm:1.1.4" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/1672c3fa5c1f2dbc354db4d403523eef15bcbe24448b911e67e3e95f351b72ffc1dc1021a225d98116cd78d1c96e6280f1645ac6d6b9930df46b180b8a6f11a5 + languageName: node + linkType: hard + +"@radix-ui/react-use-effect-event@npm:0.0.5": + version: 0.0.5 + resolution: "@radix-ui/react-use-effect-event@npm:0.0.5" + dependencies: + "@radix-ui/react-use-layout-effect": "npm:1.1.4" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/36eb3523d3c30e88615e0492aed5c644e61eb4fec98097de0fb2ee1cf33d7684b76e8a34bedb0aa7c25b412e0ef56c83b16772e9716d03d61901bc1b98cb1993 + languageName: node + linkType: hard + +"@radix-ui/react-use-is-hydrated@npm:0.1.3": + version: 0.1.3 + resolution: "@radix-ui/react-use-is-hydrated@npm:0.1.3" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/ec5d8cd7dcb825d23c9e622cb78605242ae6ae5744549f4713fd543a2a2003c356031bac3bd8db353579e6cd9f71798b59915f54de7b78b442e847024f2a5ff9 + languageName: node + linkType: hard + +"@radix-ui/react-use-layout-effect@npm:1.1.4": + version: 1.1.4 + resolution: "@radix-ui/react-use-layout-effect@npm:1.1.4" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/427df10bb3c55de36afd6b9a749fb4b72b0f78840ae4abb5dda24aa09d653ede2d264c96a8030f58db353498f42b2bce929510c777004e250f7845b06c782bcf + languageName: node + linkType: hard + +"@radix-ui/react-use-rect@npm:1.1.4": + version: 1.1.4 + resolution: "@radix-ui/react-use-rect@npm:1.1.4" + dependencies: + "@radix-ui/rect": "npm:1.1.3" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/3acfe9265f9fbdcdead05ec4540a95d8932fb53e9eabd73c080650122424d3d5247260d349ea2d91b2712af9670a27ac2110400671b98d061b2347390fe29490 + languageName: node + linkType: hard + +"@radix-ui/react-use-size@npm:1.1.4": + version: 1.1.4 + resolution: "@radix-ui/react-use-size@npm:1.1.4" + dependencies: + "@radix-ui/react-use-layout-effect": "npm:1.1.4" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/ebb4a0c6e71768ae231635cc721d7f01de0a31637e73cccea095ad63921bfece9635eb9500cbe32504585380910f578216fb3eebdfc21b511f6dd6df81659f75 + languageName: node + linkType: hard + +"@radix-ui/rect@npm:1.1.3": + version: 1.1.3 + resolution: "@radix-ui/rect@npm:1.1.3" + checksum: 10c0/9396d6c4dbd8da51c0bef7481e1e46e7f371e27cc6a55c1079e854f3aa9f7dd89e02d98cc36e861e8d43fbb775eb76b06bdf5879fa12f25116bea676af2926da + languageName: node + linkType: hard + +"@rolldown/binding-android-arm-eabi@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-android-arm-eabi@npm:1.2.9" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rolldown/binding-android-arm64@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-android-arm64@npm:1.2.9" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rolldown/binding-darwin-arm64@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-darwin-arm64@npm:1.2.9" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rolldown/binding-darwin-x64@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-darwin-x64@npm:1.2.9" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rolldown/binding-freebsd-x64@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-freebsd-x64@npm:1.2.9" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@rolldown/binding-linux-arm-gnueabihf@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-linux-arm-gnueabihf@npm:1.2.9" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@rolldown/binding-linux-arm64-gnu@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-linux-arm64-gnu@npm:1.2.9" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rolldown/binding-linux-arm64-musl@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-linux-arm64-musl@npm:1.2.9" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rolldown/binding-linux-ppc64-gnu@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-linux-ppc64-gnu@npm:1.2.9" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rolldown/binding-linux-s390x-gnu@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-linux-s390x-gnu@npm:1.2.9" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rolldown/binding-linux-x64-gnu@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-linux-x64-gnu@npm:1.2.9" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rolldown/binding-linux-x64-musl@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-linux-x64-musl@npm:1.2.9" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rolldown/binding-openharmony-arm64@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-openharmony-arm64@npm:1.2.9" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@rolldown/binding-win32-arm64-msvc@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-win32-arm64-msvc@npm:1.2.9" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rolldown/binding-win32-x64-msvc@npm:1.2.9": + version: 1.2.9 + resolution: "@rolldown/binding-win32-x64-msvc@npm:1.2.9" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rolldown/pluginutils@npm:1.0.0-beta.27": + version: 1.0.0-beta.27 + resolution: "@rolldown/pluginutils@npm:1.0.0-beta.27" + checksum: 10c0/9658f235b345201d4f6bfb1f32da9754ca164f892d1cb68154fe5f53c1df42bd675ecd409836dff46884a7847d6c00bdc38af870f7c81e05bba5c2645eb4ab9c + languageName: node + linkType: hard + +"@rolldown/pluginutils@npm:^1.0.0": + version: 1.0.1 + resolution: "@rolldown/pluginutils@npm:1.0.1" + checksum: 10c0/99d9b06d90196823e4d8c841f258db7a16e5dbba5824a2962b05d907b79f1ba929d56f22dd744fd530936e568c865ee56a719dc31e57e13bc0a8eb4764a8d8dd + languageName: node + linkType: hard + +"@rollup/pluginutils@npm:^5.0.2": + version: 5.4.0 + resolution: "@rollup/pluginutils@npm:5.4.0" + dependencies: + "@types/estree": "npm:^1.0.0" + estree-walker: "npm:^2.0.2" + picomatch: "npm:^4.0.2" + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 10c0/ccc2cbd3a05df642df60ab05ffb81b2e564bd945e2a118bb8a474ea75b941033c8f44273133d4865643cca1492d0c80b14de1281f74779a64285a80fc3a194d8 + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.63.4" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-android-arm64@npm:4.63.4" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-darwin-arm64@npm:4.63.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-darwin-x64@npm:4.63.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-arm64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.63.4" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-x64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-freebsd-x64@npm:4.63.4" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.63.4" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.63.4" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.63.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.63.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.63.4" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-musl@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-loong64-musl@npm:4.63.4" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.63.4" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-musl@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.63.4" + conditions: os=linux & cpu=ppc64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.63.4" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-musl@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.63.4" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.63.4" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.63.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.63.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-openbsd-x64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-openbsd-x64@npm:4.63.4" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-openharmony-arm64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.63.4" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.63.4" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.63.4" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.63.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.63.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@shikijs/core@npm:4.4.3": + version: 4.4.3 + resolution: "@shikijs/core@npm:4.4.3" + dependencies: + "@shikijs/primitive": "npm:4.4.3" + "@shikijs/types": "npm:4.4.3" + "@shikijs/vscode-textmate": "npm:^10.0.2" + "@types/hast": "npm:^3.0.5" + hast-util-to-html: "npm:^9.0.5" + checksum: 10c0/6ee90d70d9c2b98cadd11c1b9174851f8e761d5a47cf9723d7070546c936135e3faf1bccb9249d489e04963ffcadd07fad48385953057cbd40de49d7fd1f93a3 + languageName: node + linkType: hard + +"@shikijs/engine-javascript@npm:4.4.3": + version: 4.4.3 + resolution: "@shikijs/engine-javascript@npm:4.4.3" + dependencies: + "@shikijs/types": "npm:4.4.3" + "@shikijs/vscode-textmate": "npm:^10.0.2" + oniguruma-to-es: "npm:^4.3.6" + checksum: 10c0/22e611a783950dccb72c45c699d423897378d9bb85681fea6de2dda93e9fe238ade93c530ebe6b7c229ba23817fc64797ddecb314af4ea1e9e317f6167ee4d82 + languageName: node + linkType: hard + +"@shikijs/engine-oniguruma@npm:4.4.3": + version: 4.4.3 + resolution: "@shikijs/engine-oniguruma@npm:4.4.3" + dependencies: + "@shikijs/types": "npm:4.4.3" + "@shikijs/vscode-textmate": "npm:^10.0.2" + checksum: 10c0/119f5a7e0b660c5d3b5a15a1fb564ca8705e2e6b533e766dfd3124faf3bf3abd869c1fcf70be9b4b04865ae8d8bcb75d4185defacb2a5e1bfa66eea5d0a7d465 + languageName: node + linkType: hard + +"@shikijs/langs@npm:4.4.3": + version: 4.4.3 + resolution: "@shikijs/langs@npm:4.4.3" + dependencies: + "@shikijs/types": "npm:4.4.3" + checksum: 10c0/3150ca6b69440511bab56b089a28228206012003e105739509728964108ac7fdce36b3637f4927de401e6960bad88298a68699b92b6021e5afe8eca4102876e7 + languageName: node + linkType: hard + +"@shikijs/primitive@npm:4.4.3": + version: 4.4.3 + resolution: "@shikijs/primitive@npm:4.4.3" + dependencies: + "@shikijs/types": "npm:4.4.3" + "@shikijs/vscode-textmate": "npm:^10.0.2" + "@types/hast": "npm:^3.0.5" + checksum: 10c0/ecee74fca715882eb7dd9de0d7fd5816592285606aa21428abd87dcb5929a065638b2edf275c4199dc69191a4a719c337f522bec9c6dacfe0b63bba5fcc34a38 + languageName: node + linkType: hard + +"@shikijs/themes@npm:4.4.3": + version: 4.4.3 + resolution: "@shikijs/themes@npm:4.4.3" + dependencies: + "@shikijs/types": "npm:4.4.3" + checksum: 10c0/69b8e1f1125d493854361e9b31e89b6d7fa14df65600b07e41b581a20e2d24b5291fea4ca4ee4186186c99e8bd3d67cc4569bbf517e6372c9329d556e36ed004 + languageName: node + linkType: hard + +"@shikijs/types@npm:4.4.3": + version: 4.4.3 + resolution: "@shikijs/types@npm:4.4.3" + dependencies: + "@shikijs/vscode-textmate": "npm:^10.0.2" + "@types/hast": "npm:^3.0.5" + checksum: 10c0/ba8f0f38332d9bf44298e47acc5bf8229025464ed2dce38deddd1198d5d69a195792b29a6800bf2dfb89db67ae6d70720a48340b5ed5a4c33afebd364cca8ab7 + languageName: node + linkType: hard + +"@shikijs/vscode-textmate@npm:^10.0.2": + version: 10.0.2 + resolution: "@shikijs/vscode-textmate@npm:10.0.2" + checksum: 10c0/36b682d691088ec244de292dc8f91b808f95c89466af421cf84cbab92230f03c8348649c14b3251991b10ce632b0c715e416e992dd5f28ff3221dc2693fd9462 + languageName: node + linkType: hard + +"@simple-libs/child-process-utils@npm:^2.0.0": + version: 2.0.0 + resolution: "@simple-libs/child-process-utils@npm:2.0.0" + dependencies: + "@simple-libs/stream-utils": "npm:^2.0.0" + checksum: 10c0/8bd2ad6fe0eb7ed45e7f656b8fb2ea249673dad807ba69b950a91319747fd871c386010c766cbf8ac7242aeef76d4a03f83765c068ebb6267e2bf7e6a042e2f2 + languageName: node + linkType: hard + +"@simple-libs/stream-utils@npm:^2.0.0": + version: 2.0.0 + resolution: "@simple-libs/stream-utils@npm:2.0.0" + checksum: 10c0/27fa4a9eef3d652a80b970bee18589a618f66884cd15845a0c57d7b73f005ea4d9acda7fe580851af229d07de749419715d06dcedf33bcb1a671e7a27106fdda + languageName: node + linkType: hard + +"@storybook/addon-a11y@npm:^9.1.10": + version: 9.1.20 + resolution: "@storybook/addon-a11y@npm:9.1.20" + dependencies: + "@storybook/global": "npm:^5.0.0" + axe-core: "npm:^4.2.0" + peerDependencies: + storybook: ^9.1.20 + checksum: 10c0/0dfa7a5dbe0221bf1e78c46c7ec7536487c090890424dc8152f30021b6922e6dada6c19650d0c5ca0f38e2d788b22718bb3c9257f2dd60343daf76f5e73c8ac4 + languageName: node + linkType: hard + +"@storybook/addon-docs@npm:^9.1.10": + version: 9.1.20 + resolution: "@storybook/addon-docs@npm:9.1.20" + dependencies: + "@mdx-js/react": "npm:^3.0.0" + "@storybook/csf-plugin": "npm:9.1.20" + "@storybook/icons": "npm:^1.4.0" + "@storybook/react-dom-shim": "npm:9.1.20" + react: "npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + react-dom: "npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + ts-dedent: "npm:^2.0.0" + peerDependencies: + storybook: ^9.1.20 + checksum: 10c0/8f0569f0a985762bec3073121bcff2c2353e3d1b49be73a55d70bec3678c858c0f5d9ab731edb9e488fe7e4bb10b52b04f40812e05a6eb536766bbd5325f32f1 + languageName: node + linkType: hard + +"@storybook/builder-vite@npm:9.1.20": + version: 9.1.20 + resolution: "@storybook/builder-vite@npm:9.1.20" + dependencies: + "@storybook/csf-plugin": "npm:9.1.20" + ts-dedent: "npm:^2.0.0" + peerDependencies: + storybook: ^9.1.20 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + checksum: 10c0/66115548731615e9f66a23f69499a5334b83bc85500695b55d931fd3758dd733b9b9a806426aa6c2da49429736cf29dce57b7d574a104b9b7afb5777dae20c0a + languageName: node + linkType: hard + +"@storybook/csf-plugin@npm:9.1.20": + version: 9.1.20 + resolution: "@storybook/csf-plugin@npm:9.1.20" + dependencies: + unplugin: "npm:^1.3.1" + peerDependencies: + storybook: ^9.1.20 + checksum: 10c0/42642bf8252400f0b5a889e118dab3c35505ce539a57cdb51ae6fc335485de055d9b635db8cc8ae6d47a77ea5856cdf6880458c70b26b214ea48d44502a1bf57 + languageName: node + linkType: hard + +"@storybook/global@npm:^5.0.0": + version: 5.0.0 + resolution: "@storybook/global@npm:5.0.0" + checksum: 10c0/8f1b61dcdd3a89584540896e659af2ecc700bc740c16909a7be24ac19127ea213324de144a141f7caf8affaed017d064fea0618d453afbe027cf60f54b4a6d0b + languageName: node + linkType: hard + +"@storybook/icons@npm:^1.4.0": + version: 1.6.0 + resolution: "@storybook/icons@npm:1.6.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + checksum: 10c0/bbec9201a78a730195f9cf377b15856dc414a54d04e30d16c379d062425cc617bfd0d6586ba1716012cfbdab461f0c9693a6a52920f9bd09c7b4291fb116f59c + languageName: node + linkType: hard + +"@storybook/react-dom-shim@npm:9.1.20": + version: 9.1.20 + resolution: "@storybook/react-dom-shim@npm:9.1.20" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + storybook: ^9.1.20 + checksum: 10c0/73ee792f3c24d6dbbb8bfedf394ae6efb80d53704149406dc26cc0f564bbdbd83a35bc7fc8c80e58de7e897c75717eba8849a4970a297abe66dc32c1fffe0c7d + languageName: node + linkType: hard + +"@storybook/react-vite@npm:^9.1.10": + version: 9.1.20 + resolution: "@storybook/react-vite@npm:9.1.20" + dependencies: + "@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.6.1" + "@rollup/pluginutils": "npm:^5.0.2" + "@storybook/builder-vite": "npm:9.1.20" + "@storybook/react": "npm:9.1.20" + find-up: "npm:^7.0.0" + magic-string: "npm:^0.30.0" + react-docgen: "npm:^8.0.0" + resolve: "npm:^1.22.8" + tsconfig-paths: "npm:^4.2.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + storybook: ^9.1.20 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + checksum: 10c0/33f2fa9c7710dd0bbf846cbd40358d80b8e24b52d3d2ae76b1922be5c1cd7d21587d4611528b511b20a70006ffac7df62197aa73d79da38c61e5efababd3ff27 + languageName: node + linkType: hard + +"@storybook/react@npm:9.1.20, @storybook/react@npm:^9.1.10": + version: 9.1.20 + resolution: "@storybook/react@npm:9.1.20" + dependencies: + "@storybook/global": "npm:^5.0.0" + "@storybook/react-dom-shim": "npm:9.1.20" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + storybook: ^9.1.20 + typescript: ">= 4.9.x" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/ec7b30305c8ab03e00069aec8e9e0afed9d5392f0d2a5e00ec66cb10468425be8c1bb87c359e87e5c89b6aacb24cca123342ed706b7c72c604c5bad3d47310ee + languageName: node + linkType: hard + +"@tailwindcss/node@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/node@npm:4.3.3" + dependencies: + "@jridgewell/remapping": "npm:^2.3.5" + enhanced-resolve: "npm:^5.24.1" + jiti: "npm:^2.7.0" + lightningcss: "npm:1.32.0" + magic-string: "npm:^0.30.21" + source-map-js: "npm:^1.2.1" + tailwindcss: "npm:4.3.3" + checksum: 10c0/0123669be5b3e78d42b0e10cb34d547fb8574c8e7dc7a97a141c8a4ab4215852b5b1b99b97763e898e1da1493ee8424cb72e28225cc4876828c95156f94d7f0e + languageName: node + linkType: hard + +"@tailwindcss/oxide-android-arm64@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-android-arm64@npm:4.3.3" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-darwin-arm64@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-darwin-arm64@npm:4.3.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-darwin-x64@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-darwin-x64@npm:4.3.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-freebsd-x64@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-freebsd-x64@npm:4.3.3" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.3.3" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm64-gnu@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-linux-arm64-gnu@npm:4.3.3" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm64-musl@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-linux-arm64-musl@npm:4.3.3" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-x64-gnu@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-linux-x64-gnu@npm:4.3.3" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-x64-musl@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-linux-x64-musl@npm:4.3.3" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@tailwindcss/oxide-wasm32-wasi@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-wasm32-wasi@npm:4.3.3" + dependencies: + "@emnapi/core": "npm:^1.11.1" + "@emnapi/runtime": "npm:^1.11.1" + "@emnapi/wasi-threads": "npm:^1.2.2" + "@napi-rs/wasm-runtime": "npm:^1.1.4" + "@tybys/wasm-util": "npm:^0.10.2" + tslib: "npm:^2.8.1" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@tailwindcss/oxide-win32-arm64-msvc@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-win32-arm64-msvc@npm:4.3.3" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-win32-x64-msvc@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-win32-x64-msvc@npm:4.3.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide@npm:4.3.3" + dependencies: + "@tailwindcss/oxide-android-arm64": "npm:4.3.3" + "@tailwindcss/oxide-darwin-arm64": "npm:4.3.3" + "@tailwindcss/oxide-darwin-x64": "npm:4.3.3" + "@tailwindcss/oxide-freebsd-x64": "npm:4.3.3" + "@tailwindcss/oxide-linux-arm-gnueabihf": "npm:4.3.3" + "@tailwindcss/oxide-linux-arm64-gnu": "npm:4.3.3" + "@tailwindcss/oxide-linux-arm64-musl": "npm:4.3.3" + "@tailwindcss/oxide-linux-x64-gnu": "npm:4.3.3" + "@tailwindcss/oxide-linux-x64-musl": "npm:4.3.3" + "@tailwindcss/oxide-wasm32-wasi": "npm:4.3.3" + "@tailwindcss/oxide-win32-arm64-msvc": "npm:4.3.3" + "@tailwindcss/oxide-win32-x64-msvc": "npm:4.3.3" + dependenciesMeta: + "@tailwindcss/oxide-android-arm64": + optional: true + "@tailwindcss/oxide-darwin-arm64": + optional: true + "@tailwindcss/oxide-darwin-x64": + optional: true + "@tailwindcss/oxide-freebsd-x64": + optional: true + "@tailwindcss/oxide-linux-arm-gnueabihf": + optional: true + "@tailwindcss/oxide-linux-arm64-gnu": + optional: true + "@tailwindcss/oxide-linux-arm64-musl": + optional: true + "@tailwindcss/oxide-linux-x64-gnu": + optional: true + "@tailwindcss/oxide-linux-x64-musl": + optional: true + "@tailwindcss/oxide-wasm32-wasi": + optional: true + "@tailwindcss/oxide-win32-arm64-msvc": + optional: true + "@tailwindcss/oxide-win32-x64-msvc": + optional: true + checksum: 10c0/6c38b25226dad252347451ed8953a998432b51f1e068b25e021215b886ccf8bcf3af58faf5f4ab50368091e657ca2fa341df1deca6101651eddb567122f2acb5 + languageName: node + linkType: hard + +"@tailwindcss/vite@npm:^4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/vite@npm:4.3.3" + dependencies: + "@tailwindcss/node": "npm:4.3.3" + "@tailwindcss/oxide": "npm:4.3.3" + tailwindcss: "npm:4.3.3" + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + checksum: 10c0/bb1fe12f7a0bb8f6d6142f6bb2e96060ee853902314ef838e8d7f50cf30cc4b07adb7c2c86be2164e0d62eb581b8cc4df2d9aa13b4449f597a67f73c5772d549 + languageName: node + linkType: hard + +"@tanstack/query-core@npm:5.103.2": + version: 5.103.2 + resolution: "@tanstack/query-core@npm:5.103.2" + checksum: 10c0/52362dd214a487b2194728a79c7fd1bd7af1831f21928030866a8ad28877eaec050e7215a6f3b3a0c4fdf601644ae8edb652e4cef7270f6f82d7371d2bbe9f41 + languageName: node + linkType: hard + +"@tanstack/react-query@npm:^5.75.0": + version: 5.103.2 + resolution: "@tanstack/react-query@npm:5.103.2" + dependencies: + "@tanstack/query-core": "npm:5.103.2" + peerDependencies: + react: ^18 || ^19 + checksum: 10c0/7845c13af6761c00141ef6025c4ee385bff8f16e053f8583ba1b95db47a2c1df9764a751080981226658f45487881933517d24813f47c98238b8dbdd6acee80a + languageName: node + linkType: hard + +"@testing-library/jest-dom@npm:^6.6.3": + version: 6.9.1 + resolution: "@testing-library/jest-dom@npm:6.9.1" + dependencies: + "@adobe/css-tools": "npm:^4.4.0" + aria-query: "npm:^5.0.0" + css.escape: "npm:^1.5.1" + dom-accessibility-api: "npm:^0.6.3" + picocolors: "npm:^1.1.1" + redent: "npm:^3.0.0" + checksum: 10c0/4291ebd2f0f38d14cefac142c56c337941775a5807e2a3d6f1a14c2fbd6be76a18e498ed189e95bedc97d9e8cf1738049bc76c85b5bc5e23fae7c9e10f7b3a12 + languageName: node + linkType: hard + +"@testing-library/user-event@npm:^14.6.1": + version: 14.6.7 + resolution: "@testing-library/user-event@npm:14.6.7" + peerDependencies: + "@testing-library/dom": ">=7.21.4" + checksum: 10c0/21f5e01bea2699b069947ae06d0b0fbabe0e90d2a2206b8f56a3f46126d09bd85320cba3fd3910c4d18710b1873e6966fb9021668966c615aaa14bf35d7df3ef + languageName: node + linkType: hard + +"@trpc/client@npm:^11.19.0": + version: 11.19.0 + resolution: "@trpc/client@npm:11.19.0" + peerDependencies: + "@trpc/server": 11.19.0 + typescript: ">=5.7.2" + bin: + intent: bin/intent.js + checksum: 10c0/343aeb48bc71940049e2829a2b1e832c504a03e204e3f24a724d6f2248b3267a266029a38fdf0c2d1bdbf7b732e21bec75f264f06c1a278128cc5930ee5ffc08 + languageName: node + linkType: hard + +"@trpc/react-query@npm:^11.19.0": + version: 11.19.0 + resolution: "@trpc/react-query@npm:11.19.0" + peerDependencies: + "@tanstack/react-query": ^5.80.3 + "@trpc/client": 11.19.0 + "@trpc/server": 11.19.0 + react: ">=18.2.0" + typescript: ">=5.7.2" + checksum: 10c0/9a68061c81d5680a52019bcea0eca792242771c1a3e40212f269210e913f74440c78ca440fa0bc7cbb14abc4510595412f0a1159a1e1cd0a79d4b0d71f9bd72a + languageName: node + linkType: hard + +"@trpc/server@npm:^11.19.0": + version: 11.19.0 + resolution: "@trpc/server@npm:11.19.0" + peerDependencies: + typescript: ">=5.7.2" + bin: + intent: bin/intent.js + checksum: 10c0/5f38ffdbbc308fb71bc1ea00bf58012c0c319ec2f94dc3bffc1c9f4f9811e15b87c30405c43e9c55d05a3c8d9de1de104323cea3029ada3b4da7150ba2815273 + languageName: node + linkType: hard + +"@turbo/darwin-64@npm:2.11.2": + version: 2.11.2 + resolution: "@turbo/darwin-64@npm:2.11.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@turbo/darwin-arm64@npm:2.11.2": + version: 2.11.2 + resolution: "@turbo/darwin-arm64@npm:2.11.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@turbo/linux-64@npm:2.11.2": + version: 2.11.2 + resolution: "@turbo/linux-64@npm:2.11.2" + conditions: (os=android | os=linux) & cpu=x64 + languageName: node + linkType: hard + +"@turbo/linux-arm64@npm:2.11.2": + version: 2.11.2 + resolution: "@turbo/linux-arm64@npm:2.11.2" + conditions: (os=android | os=linux) & cpu=arm64 + languageName: node + linkType: hard + +"@turbo/windows-64@npm:2.11.2": + version: 2.11.2 + resolution: "@turbo/windows-64@npm:2.11.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@turbo/windows-arm64@npm:2.11.2": + version: 2.11.2 + resolution: "@turbo/windows-arm64@npm:2.11.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@tybys/wasm-util@npm:^0.10.2, @tybys/wasm-util@npm:^0.10.3": + version: 0.10.4 + resolution: "@tybys/wasm-util@npm:0.10.4" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/27947f254efa3f2bc41f47040f57f41c246200c6439a9fd54c69f50940e320022c320275f138e30b543ee81ce69593905dfa7ba0831577ab636f829aafa70701 + languageName: node + linkType: hard + +"@types/babel__core@npm:^7.20.5": + version: 7.20.5 + resolution: "@types/babel__core@npm:7.20.5" + dependencies: + "@babel/parser": "npm:^7.20.7" + "@babel/types": "npm:^7.20.7" + "@types/babel__generator": "npm:*" + "@types/babel__template": "npm:*" + "@types/babel__traverse": "npm:*" + checksum: 10c0/bdee3bb69951e833a4b811b8ee9356b69a61ed5b7a23e1a081ec9249769117fa83aaaf023bb06562a038eb5845155ff663e2d5c75dd95c1d5ccc91db012868ff + languageName: node + linkType: hard + +"@types/babel__generator@npm:*": + version: 7.27.0 + resolution: "@types/babel__generator@npm:7.27.0" + dependencies: + "@babel/types": "npm:^7.0.0" + checksum: 10c0/9f9e959a8792df208a9d048092fda7e1858bddc95c6314857a8211a99e20e6830bdeb572e3587ae8be5429e37f2a96fcf222a9f53ad232f5537764c9e13a2bbd + languageName: node + linkType: hard + +"@types/babel__template@npm:*": + version: 7.4.4 + resolution: "@types/babel__template@npm:7.4.4" + dependencies: + "@babel/parser": "npm:^7.1.0" + "@babel/types": "npm:^7.0.0" + checksum: 10c0/cc84f6c6ab1eab1427e90dd2b76ccee65ce940b778a9a67be2c8c39e1994e6f5bbc8efa309f6cea8dc6754994524cd4d2896558df76d92e7a1f46ecffee7112b + languageName: node + linkType: hard + +"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.20.7": + version: 7.28.0 + resolution: "@types/babel__traverse@npm:7.28.0" + dependencies: + "@babel/types": "npm:^7.28.2" + checksum: 10c0/b52d7d4e8fc6a9018fe7361c4062c1c190f5778cf2466817cb9ed19d69fbbb54f9a85ffedeb748ed8062d2cf7d4cc088ee739848f47c57740de1c48cbf0d0994 + languageName: node + linkType: hard + +"@types/chai@npm:^5.2.2": + version: 5.2.3 + resolution: "@types/chai@npm:5.2.3" + dependencies: + "@types/deep-eql": "npm:*" + assertion-error: "npm:^2.0.1" + checksum: 10c0/e0ef1de3b6f8045a5e473e867c8565788c444271409d155588504840ad1a53611011f85072188c2833941189400228c1745d78323dac13fcede9c2b28bacfb2f + languageName: node + linkType: hard + +"@types/deep-eql@npm:*": + version: 4.0.2 + resolution: "@types/deep-eql@npm:4.0.2" + checksum: 10c0/bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844 + languageName: node + linkType: hard + +"@types/doctrine@npm:^0.0.9": + version: 0.0.9 + resolution: "@types/doctrine@npm:0.0.9" + checksum: 10c0/cdaca493f13c321cf0cacd1973efc0ae74569633145d9e6fc1128f32217a6968c33bea1f858275239fe90c98f3be57ec8f452b416a9ff48b8e8c1098b20fa51c + languageName: node + linkType: hard + +"@types/estree-jsx@npm:^1.0.5": + version: 1.0.5 + resolution: "@types/estree-jsx@npm:1.0.5" + dependencies: + "@types/estree": "npm:*" + checksum: 10c0/07b354331516428b27a3ab99ee397547d47eb223c34053b48f84872fafb841770834b90cc1a0068398e7c7ccb15ec51ab00ec64b31dc5e3dbefd624638a35c6d + languageName: node + linkType: hard + +"@types/estree@npm:*, @types/estree@npm:1.0.9, @types/estree@npm:^1.0.0": + version: 1.0.9 + resolution: "@types/estree@npm:1.0.9" + checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 + languageName: node + linkType: hard + +"@types/hast@npm:^3.0.0, @types/hast@npm:^3.0.4, @types/hast@npm:^3.0.5": + version: 3.0.5 + resolution: "@types/hast@npm:3.0.5" + dependencies: + "@types/unist": "npm:*" + checksum: 10c0/f3af8594a6903a507ed191eda944af18099198d6708c29102ae17118c3e20779f6929e91ed37e033541fd28d58055d8a5910a4366dbdf976cf81b13a464741fb + languageName: node + linkType: hard + +"@types/mdast@npm:^4.0.0, @types/mdast@npm:^4.0.4": + version: 4.0.4 + resolution: "@types/mdast@npm:4.0.4" + dependencies: + "@types/unist": "npm:*" + checksum: 10c0/84f403dbe582ee508fd9c7643ac781ad8597fcbfc9ccb8d4715a2c92e4545e5772cbd0dbdf18eda65789386d81b009967fdef01b24faf6640f817287f54d9c82 + languageName: node + linkType: hard + +"@types/mdx@npm:^2.0.0": + version: 2.0.14 + resolution: "@types/mdx@npm:2.0.14" + checksum: 10c0/f1e3873c1e36e2685fb99222bf81a23c55a8e2edf49d0c45cabf2c875b0ae814cd6050cce5770ff1c6881ac747d2ad33731e1de32139b04cf181d8d7e1943ff4 + languageName: node + linkType: hard + +"@types/nlcst@npm:^2.0.0": + version: 2.0.3 + resolution: "@types/nlcst@npm:2.0.3" + dependencies: + "@types/unist": "npm:*" + checksum: 10c0/d83549aaee59681ae8fa2a78d8a1b968a41eb7c0422773dff12acbf3661e4b2b2859740c3effdad9d0cd12ea14a0ec33ca302da12106476b627e09d2a029d3c1 + languageName: node + linkType: hard + +"@types/node@npm:*": + version: 26.6.2 + resolution: "@types/node@npm:26.6.2" + dependencies: + undici-types: "npm:~8.9.0" + checksum: 10c0/bb411ff5863a79a28ffa4ebadf2499d44046aa114d25a7a6c78f57def118123a0a7dea99e8fe38849099e9dba2b41b8853a7a282037618559f58653c35e7c5c5 + languageName: node + linkType: hard + +"@types/node@npm:^22.15.29": + version: 22.20.4 + resolution: "@types/node@npm:22.20.4" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/3a17e19f7b7b7a6d6c695ef8b0a34b70f84829a1539f57d338487bc3b51ddd4ca86ffbcbcc4f1307a41de83d02c1b7dfbb16e8cb6a9c3a8360b4306ccf9ead2d + languageName: node + linkType: hard + +"@types/node@npm:^24.9.2": + version: 24.13.6 + resolution: "@types/node@npm:24.13.6" + dependencies: + undici-types: "npm:~7.18.0" + checksum: 10c0/b6302b2ede891e3a583a6e9c75390e8bc0fb8a99ea81c61ed029c244426550f1508ebae4ba1acf32840fb22a7df461f1e87b449043a8f906240b31a090866c1a + languageName: node + linkType: hard + +"@types/react-dom@npm:^19.1.5": + version: 19.3.0 + resolution: "@types/react-dom@npm:19.3.0" + peerDependencies: + "@types/react": ^19.3.0 + checksum: 10c0/ce0066d28b62bc6afdd17ca84756b8fbe0f6ffc0c8b1e60739abc30e81510da2edbc1007e6ab3d974d30f5a8c25bb0f7de8d7ce09736c909fe84c0f9aed0b946 + languageName: node + linkType: hard + +"@types/react@npm:^19.1.4": + version: 19.3.0 + resolution: "@types/react@npm:19.3.0" + dependencies: + csstype: "npm:^3.2.2" + checksum: 10c0/fbcabf303f935ca9f36ed2dcf81751cf84744098c5a683c8a62a0fa7edd31714b48d5c46b12fd0014f7f2dfe4d9ec6de428a850748eec719a70294e51029551b + languageName: node + linkType: hard + +"@types/resolve@npm:^1.20.2": + version: 1.20.6 + resolution: "@types/resolve@npm:1.20.6" + checksum: 10c0/a9b0549d816ff2c353077365d865a33655a141d066d0f5a3ba6fd4b28bc2f4188a510079f7c1f715b3e7af505a27374adce2a5140a3ece2a059aab3d6e1a4244 + languageName: node + linkType: hard + +"@types/sax@npm:^1.2.1": + version: 1.2.7 + resolution: "@types/sax@npm:1.2.7" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/d077a761a0753b079bf8279b3993948030ca86ed9125437b9b29c1de40db9b2deb7fddc369f014b58861d450e8b8cc75f163aa29dc8cea81952efbfd859168cf + languageName: node + linkType: hard + +"@types/serialize-javascript@npm:^5.0.4": + version: 5.0.4 + resolution: "@types/serialize-javascript@npm:5.0.4" + checksum: 10c0/606e2e65a5958df2379ad6d12edc883faa358eb9edbc86a6852545d5bb62ec212aec4c4741ee67d1842d00a9f537ee786070b74ee6d7a1b92c27c4f3afc5508d + languageName: node + linkType: hard + +"@types/unist@npm:*, @types/unist@npm:^3.0.0, @types/unist@npm:^3.0.3": + version: 3.0.3 + resolution: "@types/unist@npm:3.0.3" + checksum: 10c0/2b1e4adcab78388e088fcc3c0ae8700f76619dbcb4741d7d201f87e2cb346bfc29a89003cfea2d76c996e1061452e14fcd737e8b25aacf949c1f2d6b2bc3dd60 + languageName: node + linkType: hard + +"@types/ws@npm:^8.5.4": + version: 8.18.1 + resolution: "@types/ws@npm:8.18.1" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/61aff1129143fcc4312f083bc9e9e168aa3026b7dd6e70796276dcfb2c8211c4292603f9c4864fae702f2ed86e4abd4d38aa421831c2fd7f856c931a481afbab + languageName: node + linkType: hard + +"@typescript/old@npm:typescript@^6": + version: 6.0.3 + resolution: "typescript@npm:6.0.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/4a25ff5045b984370f48f196b3a0120779b1b343d40b9a68d114ea5e5fff099809b2bb777576991a63a5cd59cf7bffd96ff6fe10afcefbcb8bd6fb96ad4b6606 + languageName: node + linkType: hard + +"@typescript/typescript-aix-ppc64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-aix-ppc64@npm:7.0.2" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@typescript/typescript-darwin-arm64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-darwin-arm64@npm:7.0.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@typescript/typescript-darwin-x64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-darwin-x64@npm:7.0.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@typescript/typescript-freebsd-arm64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-freebsd-arm64@npm:7.0.2" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@typescript/typescript-freebsd-x64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-freebsd-x64@npm:7.0.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@typescript/typescript-linux-arm64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-linux-arm64@npm:7.0.2" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@typescript/typescript-linux-arm@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-linux-arm@npm:7.0.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@typescript/typescript-linux-loong64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-linux-loong64@npm:7.0.2" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@typescript/typescript-linux-mips64el@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-linux-mips64el@npm:7.0.2" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@typescript/typescript-linux-ppc64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-linux-ppc64@npm:7.0.2" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@typescript/typescript-linux-riscv64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-linux-riscv64@npm:7.0.2" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@typescript/typescript-linux-s390x@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-linux-s390x@npm:7.0.2" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@typescript/typescript-linux-x64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-linux-x64@npm:7.0.2" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@typescript/typescript-netbsd-arm64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-netbsd-arm64@npm:7.0.2" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@typescript/typescript-netbsd-x64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-netbsd-x64@npm:7.0.2" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@typescript/typescript-openbsd-arm64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-openbsd-arm64@npm:7.0.2" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@typescript/typescript-openbsd-x64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-openbsd-x64@npm:7.0.2" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@typescript/typescript-sunos-x64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-sunos-x64@npm:7.0.2" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@typescript/typescript-win32-arm64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-win32-arm64@npm:7.0.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@typescript/typescript-win32-x64@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript/typescript-win32-x64@npm:7.0.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@typescript/typescript6@npm:^6.0.2": + version: 6.0.2 + resolution: "@typescript/typescript6@npm:6.0.2" + dependencies: + "@typescript/old": "npm:typescript@^6" + bin: + tsc6: bin/tsc6 + checksum: 10c0/0c6e5c1c3a61b79f2bc61506feb82f8ce54275b2af1aebc3dd9310d7f57b1de2aca40256d018cf736c524152ae4a731f5810ff7cd028d565158d53368e681af2 + languageName: node + linkType: hard + +"@ungap/structured-clone@npm:^1.0.0": + version: 1.4.0 + resolution: "@ungap/structured-clone@npm:1.4.0" + checksum: 10c0/be74a389850d02901c836c07f10c1070d7604dab4070ba3c4b77c91665b90b4175d9f186189a02a399cf054fc051d3251418238564a4491d0164969e05c802a2 + languageName: node + linkType: hard + +"@unhead/schema-org@npm:^3.4.1": + version: 3.4.1 + resolution: "@unhead/schema-org@npm:3.4.1" + dependencies: + unhead: "npm:3.4.1" + peerDependencies: + "@unhead/react": ^3.4.1 + "@unhead/solid-js": ^3.4.1 + "@unhead/svelte": ^3.4.1 + "@unhead/vue": ^3.4.1 + peerDependenciesMeta: + "@unhead/react": + optional: true + "@unhead/solid-js": + optional: true + "@unhead/svelte": + optional: true + "@unhead/vue": + optional: true + checksum: 10c0/7a0f80bb470f8b1b6b30d86a0607c8de9b31d808fe243985b6ad2bf6653ccfdb2cf8e295dfee3e1cd3e517cf549407594da67f37695dc12f2e4ecd3dee770e5d + languageName: node + linkType: hard + +"@vitejs/plugin-react@npm:^4.7.0": + version: 4.7.0 + resolution: "@vitejs/plugin-react@npm:4.7.0" + dependencies: + "@babel/core": "npm:^7.28.0" + "@babel/plugin-transform-react-jsx-self": "npm:^7.27.1" + "@babel/plugin-transform-react-jsx-source": "npm:^7.27.1" + "@rolldown/pluginutils": "npm:1.0.0-beta.27" + "@types/babel__core": "npm:^7.20.5" + react-refresh: "npm:^0.17.0" + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + checksum: 10c0/692f23960972879485d647713663ec299c478222c96567d60285acf7c7dc5c178e71abfe9d2eefddef1eeb01514dacbc2ed68aad84628debf9c7116134734253 + languageName: node + linkType: hard + +"@vitest/expect@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/expect@npm:3.2.4" + dependencies: + "@types/chai": "npm:^5.2.2" + "@vitest/spy": "npm:3.2.4" + "@vitest/utils": "npm:3.2.4" + chai: "npm:^5.2.0" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/7586104e3fd31dbe1e6ecaafb9a70131e4197dce2940f727b6a84131eee3decac7b10f9c7c72fa5edbdb68b6f854353bd4c0fa84779e274207fb7379563b10db + languageName: node + linkType: hard + +"@vitest/mocker@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/mocker@npm:3.2.4" + dependencies: + "@vitest/spy": "npm:3.2.4" + estree-walker: "npm:^3.0.3" + magic-string: "npm:^0.30.17" + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + checksum: 10c0/f7a4aea19bbbf8f15905847ee9143b6298b2c110f8b64789224cb0ffdc2e96f9802876aa2ca83f1ec1b6e1ff45e822abb34f0054c24d57b29ab18add06536ccd + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/pretty-format@npm:3.2.4" + dependencies: + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/5ad7d4278e067390d7d633e307fee8103958806a419ca380aec0e33fae71b44a64415f7a9b4bc11635d3c13d4a9186111c581d3cef9c65cc317e68f077456887 + languageName: node + linkType: hard + +"@vitest/spy@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/spy@npm:3.2.4" + dependencies: + tinyspy: "npm:^4.0.3" + checksum: 10c0/6ebf0b4697dc238476d6b6a60c76ba9eb1dd8167a307e30f08f64149612fd50227682b876420e4c2e09a76334e73f72e3ebf0e350714dc22474258292e202024 + languageName: node + linkType: hard + +"@vitest/utils@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/utils@npm:3.2.4" + dependencies: + "@vitest/pretty-format": "npm:3.2.4" + loupe: "npm:^3.1.4" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/024a9b8c8bcc12cf40183c246c244b52ecff861c6deb3477cbf487ac8781ad44c68a9c5fd69f8c1361878e55b97c10d99d511f2597f1f7244b5e5101d028ba64 + languageName: node + linkType: hard + +"@volar/kit@npm:~2.4.28": + version: 2.4.28 + resolution: "@volar/kit@npm:2.4.28" + dependencies: + "@volar/language-service": "npm:2.4.28" + "@volar/typescript": "npm:2.4.28" + typesafe-path: "npm:^0.2.2" + vscode-languageserver-textdocument: "npm:^1.0.11" + vscode-uri: "npm:^3.0.8" + peerDependencies: + typescript: "*" + checksum: 10c0/c097b930eeb94d432ba7d21e0286ec787931d9fbf1c07e47e0df2d01dfa5a8d9413bac5c2fb4e342edfe57bec29b916427e62d40792ba39b614a2ff1fe3907a1 + languageName: node + linkType: hard + +"@volar/language-core@npm:2.4.28, @volar/language-core@npm:~2.4.28": + version: 2.4.28 + resolution: "@volar/language-core@npm:2.4.28" + dependencies: + "@volar/source-map": "npm:2.4.28" + checksum: 10c0/d41f7327fed7fa5301fbf2d8f96753d645a976b21dbbeb869794a780aa6523d1e6bf258242bc3d8ccd37f8e8b98a04fea9574e6f63badc585a8a3c2e068c4a86 + languageName: node + linkType: hard + +"@volar/language-server@npm:~2.4.28": + version: 2.4.28 + resolution: "@volar/language-server@npm:2.4.28" + dependencies: + "@volar/language-core": "npm:2.4.28" + "@volar/language-service": "npm:2.4.28" + "@volar/typescript": "npm:2.4.28" + path-browserify: "npm:^1.0.1" + request-light: "npm:^0.7.0" + vscode-languageserver: "npm:^9.0.1" + vscode-languageserver-protocol: "npm:^3.17.5" + vscode-languageserver-textdocument: "npm:^1.0.11" + vscode-uri: "npm:^3.0.8" + checksum: 10c0/8266fdab264dbe449501e7d05e584069e6eb6cc5a06bf02968107c3fa32f8eb49b363d57d7abd76e5f637265469be8e13ebce1dd4b3e6ebdfae030691879d283 + languageName: node + linkType: hard + +"@volar/language-service@npm:2.4.28, @volar/language-service@npm:~2.4.28": + version: 2.4.28 + resolution: "@volar/language-service@npm:2.4.28" + dependencies: + "@volar/language-core": "npm:2.4.28" + vscode-languageserver-protocol: "npm:^3.17.5" + vscode-languageserver-textdocument: "npm:^1.0.11" + vscode-uri: "npm:^3.0.8" + checksum: 10c0/a150d34f0c1c92641ff978eb6045ac6f0717cf16a51cfd3d0798267ccdc91aca21e6746403bf0254c747fa510ea92e3eb064acfa42320c0ebae959fd4a88add8 + languageName: node + linkType: hard + +"@volar/source-map@npm:2.4.28": + version: 2.4.28 + resolution: "@volar/source-map@npm:2.4.28" + checksum: 10c0/24b0b02c7f66febe47f0bfda4a5ed4beaf949041eddc6325c7478b900faeb071795b696d97a4f326dde47217d06e40b67129300bc544f054772c5cb84c2f254e + languageName: node + linkType: hard + +"@volar/typescript@npm:2.4.28": + version: 2.4.28 + resolution: "@volar/typescript@npm:2.4.28" + dependencies: + "@volar/language-core": "npm:2.4.28" + path-browserify: "npm:^1.0.1" + vscode-uri: "npm:^3.0.8" + checksum: 10c0/075c890b9ec1cb17f17e38aaed035f8ee7d507439e87270d8e3c394356fc9387fd0bda9ec1069b36ea4c378d9375a08f5bc64c063a83427010ddd86d472124fc + languageName: node + linkType: hard + +"@vscode/emmet-helper@npm:^2.9.3": + version: 2.11.0 + resolution: "@vscode/emmet-helper@npm:2.11.0" + dependencies: + emmet: "npm:^2.4.3" + jsonc-parser: "npm:^2.3.0" + vscode-languageserver-textdocument: "npm:^1.0.1" + vscode-languageserver-types: "npm:^3.15.1" + vscode-uri: "npm:^3.0.8" + checksum: 10c0/41c743258f958ff143f5152a7c8e8e1c6f96336f4147d537ace7c81337d5f9d7f93efd6df16c80e6d9506c51df9c8deb6440765a1deae219e31cdbead3cfd841 + languageName: node + linkType: hard + +"@vscode/l10n@npm:^0.0.18": + version: 0.0.18 + resolution: "@vscode/l10n@npm:0.0.18" + checksum: 10c0/d1fc797001f7d508ab3fa91175f7a50ea98516c4e47830ff2be79163cde9279279514a167a3bad15b7ab7fc243e7808d8f32d3eb41f4a7d6721d9dfdbb38d89e + languageName: node + linkType: hard + +"abbrev@npm:^5.0.0": + version: 5.0.0 + resolution: "abbrev@npm:5.0.0" + checksum: 10c0/8e88f5c798ea4562d28c5a3e9ad69e3879890bc5d695d8f2dffb8609be4c890aacc8f80ef4553fdd2c6a62d70c2ce8bc57b38074e383beb7487bdafa9ed42ea5 + languageName: node + linkType: hard + +"acorn@npm:^8.14.0": + version: 8.18.0 + resolution: "acorn@npm:8.18.0" + bin: + acorn: bin/acorn + checksum: 10c0/be771be2135cc07910cf76f444ad514d7dcfd6d4a8026e597e93155275abc8ef61eee12211d52146e9d962874269b634f397464942087be013c89d0c54c5f8e5 + languageName: node + linkType: hard + +"ajv-draft-04@npm:^1.0.0": + version: 1.0.0 + resolution: "ajv-draft-04@npm:1.0.0" + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + checksum: 10c0/6044310bd38c17d77549fd326bd40ce1506fa10b0794540aa130180808bf94117fac8c9b448c621512bea60e4a947278f6a978e87f10d342950c15b33ddd9271 + languageName: node + linkType: hard + +"ajv-i18n@npm:^4.2.0": + version: 4.2.0 + resolution: "ajv-i18n@npm:4.2.0" + peerDependencies: + ajv: ^8.0.0-beta.0 + checksum: 10c0/8c27882f2d46147600538aa8f48a633c72cd65baa37e0fae7159e832122b4ee504f4076f81ea3f78c15d7236a2d499d42a34d1a27b202066edc620f23963f6db + languageName: node + linkType: hard + +"ajv@npm:^8.11.0, ajv@npm:^8.17.1": + version: 8.20.0 + resolution: "ajv@npm:8.20.0" + dependencies: + fast-deep-equal: "npm:^3.1.3" + fast-uri: "npm:^3.0.1" + json-schema-traverse: "npm:^1.0.0" + require-from-string: "npm:^2.0.2" + checksum: 10c0/5df9a1c8f83863cde1bd3a9ddb426f599718f88e3dc9153616c79fb28e0be455335830d7f21d745576519f057b371352daa31047b6a33d7036fe08777d60cf2a + languageName: node + linkType: hard + +"am-i-vibing@npm:^0.4.0": + version: 0.4.0 + resolution: "am-i-vibing@npm:0.4.0" + dependencies: + process-ancestry: "npm:^0.1.0" + bin: + am-i-vibing: dist/cli.mjs + checksum: 10c0/af511ee621c78957e7e9e86b15498d64b9d73c54689e571a03531fe123002d3a5f7bd2d7fadda9cc07511c8f7cfdcc888d897bebeb12d2bc8b725161e64fd3f5 + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 + languageName: node + linkType: hard + +"ansi-regex@npm:^6.2.2": + version: 6.3.0 + resolution: "ansi-regex@npm:6.3.0" + checksum: 10c0/bc047786d0531f1f22d1e22e9863e8554488a399f1ee5270b753efa6de938a788d27456f2b256770f11fdd8b1e847483bc1b55ddc2b3ffe82ec45c875292c651 + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: "npm:^2.0.1" + checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 + languageName: node + linkType: hard + +"ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": + version: 6.2.3 + resolution: "ansi-styles@npm:6.2.3" + checksum: 10c0/23b8a4ce14e18fb854693b95351e286b771d23d8844057ed2e7d083cd3e708376c3323707ec6a24365f7d7eda3ca00327fe04092e29e551499ec4c8b7bfac868 + languageName: node + linkType: hard + +"anymatch@npm:^3.1.3": + version: 3.1.3 + resolution: "anymatch@npm:3.1.3" + dependencies: + normalize-path: "npm:^3.0.0" + picomatch: "npm:^2.0.4" + checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac + languageName: node + linkType: hard + +"app-template@workspace:.": + version: 0.0.0-use.local + resolution: "app-template@workspace:." + dependencies: + "@astrojs/check": "npm:^0.9.10" + "@biomejs/biome": "npm:^2.1.1" + "@commitlint/cli": "npm:^21.2.3" + "@commitlint/config-conventional": "npm:^21.2.3" + "@emnapi/runtime": "npm:^1.11.3" + "@storybook/addon-a11y": "npm:^9.1.10" + "@storybook/addon-docs": "npm:^9.1.10" + "@storybook/react": "npm:^9.1.10" + "@storybook/react-vite": "npm:^9.1.10" + "@typescript/typescript6": "npm:^6.0.2" + lefthook: "npm:^2.1.14" + storybook: "npm:^9.1.10" + turbo: "npm:^2.11.2" + typescript: "npm:^7.0.2" + languageName: unknown + linkType: soft + +"arg@npm:^5.0.0": + version: 5.0.2 + resolution: "arg@npm:5.0.2" + checksum: 10c0/ccaf86f4e05d342af6666c569f844bec426595c567d32a8289715087825c2ca7edd8a3d204e4d2fb2aa4602e09a57d0c13ea8c9eea75aac3dbb4af5514e6800e + languageName: node + linkType: hard + +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 10c0/c5640c2d89045371c7cedd6a70212a04e360fd34d6edeae32f6952c63949e3525ea77dbec0289d8213a99bbaeab5abfa860b5c12cf88a2e6cf8106e90dd27a7e + languageName: node + linkType: hard + +"argue-cli@npm:^3.1.0": + version: 3.2.0 + resolution: "argue-cli@npm:3.2.0" + checksum: 10c0/1b3df0c0e0a7a6199b45124aa2e664d9ec467b484cc9e9263ecf3062cc30725548fd840688ceee8bf5d67955cad8d155111e80ec6626d60b884b6c66264e9940 + languageName: node + linkType: hard + +"aria-hidden@npm:^1.2.4": + version: 1.2.6 + resolution: "aria-hidden@npm:1.2.6" + dependencies: + tslib: "npm:^2.0.0" + checksum: 10c0/7720cb539497a9f760f68f98a4b30f22c6767aa0e72fa7d58279f7c164e258fc38b2699828f8de881aab0fc8e9c56d1313a3f1a965046fc0381a554dbc72b54a + languageName: node + linkType: hard + +"aria-query@npm:^5.0.0, aria-query@npm:^5.3.2": + version: 5.3.2 + resolution: "aria-query@npm:5.3.2" + checksum: 10c0/003c7e3e2cff5540bf7a7893775fc614de82b0c5dde8ae823d47b7a28a9d4da1f7ed85f340bdb93d5649caa927755f0e31ecc7ab63edfdfc00c8ef07e505e03e + languageName: node + linkType: hard + +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: 10c0/bbbcb117ac6480138f8c93cf7f535614282dea9dc828f540cdece85e3c665e8f78958b96afac52f29ff883c72638e6a87d469ecc9fe5bc902df03ed24a55dba8 + languageName: node + linkType: hard + +"ast-types@npm:^0.16.1": + version: 0.16.3 + resolution: "ast-types@npm:0.16.3" + dependencies: + tslib: "npm:^2.0.1" + checksum: 10c0/a49ff15529aa4545d44b28064d50769c3b48f608f30a388e1da3ac659e11c95502a1dd4345fb2209093007a214ab912110ef13e14a353781fccd74f4222980c6 + languageName: node + linkType: hard + +"astro@npm:^7.3.3": + version: 7.3.3 + resolution: "astro@npm:7.3.3" + dependencies: + "@astrojs/compiler-rs": "npm:^0.4.0" + "@astrojs/internal-helpers": "npm:0.11.0" + "@astrojs/markdown-satteri": "npm:0.4.1" + "@astrojs/telemetry": "npm:3.3.3" + "@capsizecss/unpack": "npm:^4.0.0" + "@clack/prompts": "npm:^1.1.0" + "@oslojs/encoding": "npm:^1.1.0" + am-i-vibing: "npm:^0.4.0" + aria-query: "npm:^5.3.2" + axobject-query: "npm:^4.1.0" + ci-info: "npm:^4.4.0" + clsx: "npm:^2.1.1" + common-ancestor-path: "npm:^2.0.0" + cookie: "npm:^2.0.1" + devalue: "npm:^5.8.1" + diff: "npm:^9.0.0" + dset: "npm:^3.1.4" + es-module-lexer: "npm:^2.0.0" + esbuild: "npm:^0.28.0" + find-proc: "npm:0.2.0" + flattie: "npm:^1.1.1" + fontace: "npm:~0.4.1" + get-tsconfig: "npm:5.0.0-beta.4" + github-slugger: "npm:^2.0.0" + html-escaper: "npm:3.0.3" + http-cache-semantics: "npm:^4.2.0" + js-yaml: "npm:^4.3.0" + jsonc-parser: "npm:^3.3.1" + magic-string: "npm:^1.0.0" + magicast: "npm:^0.5.2" + mrmime: "npm:^2.0.1" + neotraverse: "npm:^1.0.1" + obug: "npm:^2.1.1" + p-limit: "npm:^7.3.0" + p-queue: "npm:^9.1.0" + package-manager-detector: "npm:^1.6.0" + piccolore: "npm:^0.1.3" + picomatch: "npm:^4.0.4" + sharp: "npm:^0.35.4" + shiki: "npm:^4.0.2" + smol-toml: "npm:^1.6.0" + svgo: "npm:^4.0.2" + tinyclip: "npm:^0.1.12" + tinyexec: "npm:^1.0.4" + tinyglobby: "npm:^0.2.15" + ultrahtml: "npm:^1.6.0" + unifont: "npm:~0.7.5" + unstorage: "npm:^1.17.5" + verkit: "npm:^0.4.0" + vite: "npm:^8.0.13" + vitefu: "npm:^1.1.2" + xxhash-wasm: "npm:^1.1.0" + yargs-parser: "npm:^22.0.0" + zod: "npm:^4.5.4" + peerDependencies: + "@astrojs/markdown-remark": ^7.3.0 + dependenciesMeta: + sharp: + optional: true + peerDependenciesMeta: + "@astrojs/markdown-remark": + optional: true + bin: + astro: ./bin/astro.mjs + checksum: 10c0/6b34cf49a3e86fcbc43427b3ebfa2c1aeaebb7c391e9ed4b097b8f07be91998d2ea65a99bad486e8a505e25fe3d546a903603ab83ad8e667647a6d4058c02182 + languageName: node + linkType: hard + +"axe-core@npm:^4.2.0": + version: 4.13.0 + resolution: "axe-core@npm:4.13.0" + checksum: 10c0/0e3be8be10eea64beddefd3007e1425424ff60c26c5f4d5f748ec48c2825356839c02ad4167fc1f03a6d84ecd82108f49fb2846ab7fe949273787391745561ca + languageName: node + linkType: hard + +"axobject-query@npm:^4.1.0": + version: 4.1.0 + resolution: "axobject-query@npm:4.1.0" + checksum: 10c0/c470e4f95008f232eadd755b018cb55f16c03ccf39c027b941cd8820ac6b68707ce5d7368a46756db4256fbc91bb4ead368f84f7fb034b2b7932f082f6dc0775 + languageName: node + linkType: hard + +"bail@npm:^2.0.0": + version: 2.0.2 + resolution: "bail@npm:2.0.2" + checksum: 10c0/25cbea309ef6a1f56214187004e8f34014eb015713ea01fa5b9b7e9e776ca88d0fdffd64143ac42dc91966c915a4b7b683411b56e14929fad16153fc026ffb8b + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee + languageName: node + linkType: hard + +"baseline-browser-mapping@npm:^2.11.23": + version: 2.11.25 + resolution: "baseline-browser-mapping@npm:2.11.25" + bin: + baseline-browser-mapping: dist/cli.cjs + checksum: 10c0/d725a78420b3c8b0bf401d2c724ff662e7e1b67a123b17bb4417bbd6f6f00d8245bdbda862edbca84ed65618aa80a184fec83ad5d6f7c4031cced4b8ab2e7f11 + languageName: node + linkType: hard + +"better-opn@npm:^3.0.2": + version: 3.0.2 + resolution: "better-opn@npm:3.0.2" + dependencies: + open: "npm:^8.0.4" + checksum: 10c0/911ef25d44da75aabfd2444ce7a4294a8000ebcac73068c04a60298b0f7c7506b60421aa4cd02ac82502fb42baaff7e4892234b51e6923eded44c5a11185f2f5 + languageName: node + linkType: hard + +"boolbase@npm:^1.0.0": + version: 1.0.0 + resolution: "boolbase@npm:1.0.0" + checksum: 10c0/e4b53deb4f2b85c52be0e21a273f2045c7b6a6ea002b0e139c744cb6f95e9ec044439a52883b0d74dedd1ff3da55ed140cfdddfed7fb0cccbed373de5dce1bcf + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.2": + version: 2.1.7 + resolution: "brace-expansion@npm:2.1.7" + dependencies: + balanced-match: "npm:^1.0.0" + checksum: 10c0/17ee4194c85b05cde0a7b7c068eeef664440c027116ef42c3d35c13ebabdc9fd3eaf05c5c8a06dd0ca20ae012d652561d8ac2a699daa5cbee1f0510e5134ab4d + languageName: node + linkType: hard + +"browserslist@npm:^4.24.0": + version: 4.29.0 + resolution: "browserslist@npm:4.29.0" + dependencies: + baseline-browser-mapping: "npm:^2.11.23" + caniuse-lite: "npm:^1.0.30001810" + electron-to-chromium: "npm:^1.5.427" + node-releases: "npm:^2.0.55" + update-browserslist-db: "npm:^1.3.3" + bin: + browserslist: cli.js + checksum: 10c0/0490dad3a4b8e9b187fa1ba79fa84f5753dc148020c298026a6af2a3ad279a419d4507a7a42c0754dbae0ff343c2db3dfa4b9a5e9766b3d60029458dc17b66ae + languageName: node + linkType: hard + +"buffer-from@npm:^1.0.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 10c0/124fff9d66d691a86d3b062eff4663fe437a9d9ee4b47b1b9e97f5a5d14f6d5399345db80f796827be7c95e70a8e765dd404b7c3ff3b3324f98e9b0c8826cc34 + languageName: node + linkType: hard + +"callsites@npm:^3.0.0": + version: 3.1.0 + resolution: "callsites@npm:3.1.0" + checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001810": + version: 1.0.30001810 + resolution: "caniuse-lite@npm:1.0.30001810" + checksum: 10c0/d29bc73f33c888025d54c7ff2984372d3b350b15a6a9e174884fb40976175e9177f202e2a72c2d428dddfc032784bc8ba635a31d168fcab9fac65756d7b3d6b4 + languageName: node + linkType: hard + +"ccount@npm:^2.0.0": + version: 2.0.1 + resolution: "ccount@npm:2.0.1" + checksum: 10c0/3939b1664390174484322bc3f45b798462e6c07ee6384cb3d645e0aa2f318502d174845198c1561930e1d431087f74cf1fe291ae9a4722821a9f4ba67e574350 + languageName: node + linkType: hard + +"chai@npm:^5.2.0": + version: 5.3.3 + resolution: "chai@npm:5.3.3" + dependencies: + assertion-error: "npm:^2.0.1" + check-error: "npm:^2.1.1" + deep-eql: "npm:^5.0.1" + loupe: "npm:^3.1.0" + pathval: "npm:^2.0.0" + checksum: 10c0/b360fd4d38861622e5010c2f709736988b05c7f31042305fa3f4e9911f6adb80ccfb4e302068bf8ed10e835c2e2520cba0f5edc13d878b886987e5aa62483f53 + languageName: node + linkType: hard + +"character-entities-html4@npm:^2.0.0": + version: 2.1.0 + resolution: "character-entities-html4@npm:2.1.0" + checksum: 10c0/fe61b553f083400c20c0b0fd65095df30a0b445d960f3bbf271536ae6c3ba676f39cb7af0b4bf2755812f08ab9b88f2feed68f9aebb73bb153f7a115fe5c6e40 + languageName: node + linkType: hard + +"character-entities-legacy@npm:^3.0.0": + version: 3.0.0 + resolution: "character-entities-legacy@npm:3.0.0" + checksum: 10c0/ec4b430af873661aa754a896a2b55af089b4e938d3d010fad5219299a6b6d32ab175142699ee250640678cd64bdecd6db3c9af0b8759ab7b155d970d84c4c7d1 + languageName: node + linkType: hard + +"character-entities@npm:^2.0.0": + version: 2.0.2 + resolution: "character-entities@npm:2.0.2" + checksum: 10c0/b0c645a45bcc90ff24f0e0140f4875a8436b8ef13b6bcd31ec02cfb2ca502b680362aa95386f7815bdc04b6464d48cf191210b3840d7c04241a149ede591a308 + languageName: node + linkType: hard + +"check-error@npm:^2.1.1": + version: 2.1.3 + resolution: "check-error@npm:2.1.3" + checksum: 10c0/878e99038fb6476316b74668cd6a498c7e66df3efe48158fa40db80a06ba4258742ac3ee2229c4a2a98c5e73f5dff84eb3e50ceb6b65bbd8f831eafc8338607d + languageName: node + linkType: hard + +"chokidar@npm:^4.0.3": + version: 4.0.3 + resolution: "chokidar@npm:4.0.3" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10c0/a58b9df05bb452f7d105d9e7229ac82fa873741c0c40ddcc7bb82f8a909fbe3f7814c9ebe9bc9a2bef9b737c0ec6e2d699d179048ef06ad3ec46315df0ebe6ad + languageName: node + linkType: hard + +"chokidar@npm:^5.0.0": + version: 5.0.0 + resolution: "chokidar@npm:5.0.0" + dependencies: + readdirp: "npm:^5.0.0" + checksum: 10c0/42fc907cb2a7ff5c9e220f84dae75380a77997f851c2a5e7865a2cf9ae45dd407a23557208cdcdbf3ac8c93341135a1748e4c48c31855f3bfa095e5159b6bdec + languageName: node + linkType: hard + +"chownr@npm:^3.0.0": + version: 3.0.0 + resolution: "chownr@npm:3.0.0" + checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10 + languageName: node + linkType: hard + +"ci-info@npm:^4.4.0": + version: 4.4.0 + resolution: "ci-info@npm:4.4.0" + checksum: 10c0/44156201545b8dde01aa8a09ee2fe9fc7a73b1bef9adbd4606c9f61c8caeeb73fb7a575c88b0443f7b4edb5ee45debaa59ed54ba5f99698339393ca01349eb3a + languageName: node + linkType: hard + +"class-variance-authority@npm:^0.7.1": + version: 0.7.1 + resolution: "class-variance-authority@npm:0.7.1" + dependencies: + clsx: "npm:^2.1.1" + checksum: 10c0/0f438cea22131808b99272de0fa933c2532d5659773bfec0c583de7b3f038378996d3350683426b8e9c74a6286699382106d71fbec52f0dd5fbb191792cccb5b + languageName: node + linkType: hard + +"cliui@npm:^9.0.1": + version: 9.0.1 + resolution: "cliui@npm:9.0.1" + dependencies: + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + wrap-ansi: "npm:^9.0.0" + checksum: 10c0/13441832e9efe7c7a76bd2b8e683555c478d461a9f249dc5db9b17fe8d4b47fa9277b503914b90bd00e4a151abb6b9b02b2288972ffe2e5e3ca40bcb1c2330d3 + languageName: node + linkType: hard + +"clsx@npm:^2.1.1": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839 + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: "npm:~1.1.4" + checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 + languageName: node + linkType: hard + +"comma-separated-tokens@npm:^2.0.0": + version: 2.0.3 + resolution: "comma-separated-tokens@npm:2.0.3" + checksum: 10c0/91f90f1aae320f1755d6957ef0b864fe4f54737f3313bd95e0802686ee2ca38bff1dd381964d00ae5db42912dd1f4ae5c2709644e82706ffc6f6842a813cdd67 + languageName: node + linkType: hard + +"commander@npm:^11.1.0": + version: 11.1.0 + resolution: "commander@npm:11.1.0" + checksum: 10c0/13cc6ac875e48780250f723fb81c1c1178d35c5decb1abb1b628b3177af08a8554e76b2c0f29de72d69eef7c864d12613272a71fabef8047922bc622ab75a179 + languageName: node + linkType: hard + +"common-ancestor-path@npm:^2.0.0": + version: 2.0.0 + resolution: "common-ancestor-path@npm:2.0.0" + checksum: 10c0/fa0872dc8d5ffb2c0bb006d1f9e7ba4586773df4f0cf3dfa4b4c95710cedb8a78246fbbcc1392c71c882bd5428a2d003851bdd9033f549a445ac2c5deacb45ca + languageName: node + linkType: hard + +"conventional-changelog-angular@npm:^9.0.0": + version: 9.4.0 + resolution: "conventional-changelog-angular@npm:9.4.0" + dependencies: + "@conventional-changelog/template": "npm:^1.4.0" + checksum: 10c0/79368f0bb9a0b10cc38b5eb0af64b7a69c815f3e5fed853af9f80b56b8f09b1578d0fb47d8a0cf13e8030c6dedb81a0f507d1998cd064d2ade3f517c82fbdcde + languageName: node + linkType: hard + +"conventional-changelog-conventionalcommits@npm:^10.0.0": + version: 10.4.0 + resolution: "conventional-changelog-conventionalcommits@npm:10.4.0" + dependencies: + "@conventional-changelog/template": "npm:^1.4.0" + checksum: 10c0/60d99d74e6fdba134fc4785c13f87df82b9cabd980b4390dc958a8abf75d6fe1fdad921f52fb75e02f01a376cfd194b9f506f1e7718041e522b5260b53273882 + languageName: node + linkType: hard + +"conventional-commits-parser@npm:^7.0.0": + version: 7.1.2 + resolution: "conventional-commits-parser@npm:7.1.2" + dependencies: + "@simple-libs/stream-utils": "npm:^2.0.0" + argue-cli: "npm:^3.1.0" + bin: + conventional-commits-parser: ./dist/cli/index.js + checksum: 10c0/dd1478a5ef4fddd52cdd67fa4a97d1f47908a20d22287ccfa3f5e09b8298a3bcf3eb49ed117744805cc07ca31edf8835ce0913e96b1bcf7c385cf9bcefb3cf54 + languageName: node + linkType: hard + +"convert-source-map@npm:^2.0.0": + version: 2.0.0 + resolution: "convert-source-map@npm:2.0.0" + checksum: 10c0/8f2f7a27a1a011cc6cc88cc4da2d7d0cfa5ee0369508baae3d98c260bb3ac520691464e5bbe4ae7cdf09860c1d69ecc6f70c63c6e7c7f7e3f18ec08484dc7d9b + languageName: node + linkType: hard + +"cookie-es@npm:^1.2.3": + version: 1.2.3 + resolution: "cookie-es@npm:1.2.3" + checksum: 10c0/429eae6f5130a7380ea024d787d7e1ecc644ca84f9c43dfb70f18761a831d2ba591d28f837ce350892cfff0857d711f3a4ad93a082637bceb478823c339c1a97 + languageName: node + linkType: hard + +"cookie@npm:^2.0.1": + version: 2.0.1 + resolution: "cookie@npm:2.0.1" + checksum: 10c0/dcf3bd6ec9d0f4f32be7575e23242866cb53be55fc3d4cf36215638596661cec1327a3b4ca286ba15eba8f2abd443e422b8c5f708af4ea9f75b4d3dedc67ee4c + languageName: node + linkType: hard + +"cosmiconfig-typescript-loader@npm:^6.1.0": + version: 6.3.0 + resolution: "cosmiconfig-typescript-loader@npm:6.3.0" + dependencies: + jiti: "npm:2.6.1" + peerDependencies: + "@types/node": "*" + cosmiconfig: ">=9" + typescript: ">=5" + checksum: 10c0/dd53519bf59b31c32a831f1140eaa44f4f0bf49229b7e3ba27bb6f26097c3ecff955a490e70b31349049463066b5047bd129ab82ed078be9b1f1f22ccb776c6a + languageName: node + linkType: hard + +"cosmiconfig@npm:^9.0.1": + version: 9.0.2 + resolution: "cosmiconfig@npm:9.0.2" + dependencies: + env-paths: "npm:^2.2.1" + import-fresh: "npm:^3.3.0" + js-yaml: "npm:^4.1.0" + parse-json: "npm:^5.2.0" + peerDependencies: + typescript: ">=4.9.5" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/132d32863c0b3d9ace7010a10011e09089532b36e3b11e02004d671dcbd8b8f2f16293b82d510d9c15890f30358baf8722127c7b1c011449c90b6a178f9c6594 + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.6": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 + languageName: node + linkType: hard + +"crossws@npm:^0.3.5": + version: 0.3.5 + resolution: "crossws@npm:0.3.5" + dependencies: + uncrypto: "npm:^0.1.3" + checksum: 10c0/9e873546f0806606c4f775219f6811768fc3b3b0765ca8230722e849058ad098318af006e1faa39a8008c03009c37c519f6bccad41b0d78586237585c75fb38b + languageName: node + linkType: hard + +"css-select@npm:^6.0.0": + version: 6.0.0 + resolution: "css-select@npm:6.0.0" + dependencies: + boolbase: "npm:^1.0.0" + css-what: "npm:^7.0.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.2.2" + nth-check: "npm:^2.1.1" + checksum: 10c0/2f63e954b8a9475f25ea3ff23d20c556a99e464113e7dff53711bc63e5b579f17ebc82fe49d6443aa65f656e25f97e2b32b9e8143181fe22758536730b6999ca + languageName: node + linkType: hard + +"css-tree@npm:^3.0.1, css-tree@npm:^3.1.0": + version: 3.2.1 + resolution: "css-tree@npm:3.2.1" + dependencies: + mdn-data: "npm:2.27.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/1f65e9ccaa56112a4706d6f003dd43d777f0dbcf848e66fd320f823192533581f8dd58daa906cb80622658332d50284d6be13b87a6ab4556cbbfe9ef535bbf7e + languageName: node + linkType: hard + +"css-tree@npm:~2.2.0": + version: 2.2.1 + resolution: "css-tree@npm:2.2.1" + dependencies: + mdn-data: "npm:2.0.28" + source-map-js: "npm:^1.0.1" + checksum: 10c0/47e87b0f02f8ac22f57eceb65c58011dd142d2158128882a0bf963cf2eabb81a4ebbc2e3790c8289be7919fa8b83750c7b69272bd66772c708143b772ba3c186 + languageName: node + linkType: hard + +"css-what@npm:^7.0.0": + version: 7.0.0 + resolution: "css-what@npm:7.0.0" + checksum: 10c0/590329e773d8103dcc4f2aefa48d824d5b934b53baae7d8bcc3b61f0a917a0d6b6a1154d7319c6416421e7eb3b1c94704ec21924c485f4b174f0218416211fd9 + languageName: node + linkType: hard + +"css.escape@npm:^1.5.1": + version: 1.5.1 + resolution: "css.escape@npm:1.5.1" + checksum: 10c0/5e09035e5bf6c2c422b40c6df2eb1529657a17df37fda5d0433d722609527ab98090baf25b13970ca754079a0f3161dd3dfc0e743563ded8cfa0749d861c1525 + languageName: node + linkType: hard + +"csso@npm:^5.0.5": + version: 5.0.5 + resolution: "csso@npm:5.0.5" + dependencies: + css-tree: "npm:~2.2.0" + checksum: 10c0/ab4beb1e97dd7e207c10e9925405b45f15a6cd1b4880a8686ad573aa6d476aed28b4121a666cffd26c37a26179f7b54741f7c257543003bfb244d06a62ad569b + languageName: node + linkType: hard + +"csstype@npm:^3.2.2": + version: 3.2.3 + resolution: "csstype@npm:3.2.3" + checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce + languageName: node + linkType: hard + +"data-uri-to-buffer@npm:^4.0.0": + version: 4.0.1 + resolution: "data-uri-to-buffer@npm:4.0.1" + checksum: 10c0/20a6b93107597530d71d4cb285acee17f66bcdfc03fd81040921a81252f19db27588d87fc8fc69e1950c55cfb0bf8ae40d0e5e21d907230813eb5d5a7f9eb45b + languageName: node + linkType: hard + +"debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.4, debug@npm:^4.4.3": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 + languageName: node + linkType: hard + +"decode-named-character-reference@npm:^1.0.0": + version: 1.3.0 + resolution: "decode-named-character-reference@npm:1.3.0" + dependencies: + character-entities: "npm:^2.0.0" + checksum: 10c0/787f4c87f3b82ea342aa7c2d7b1882b6fb9511bb77f72ae44dcaabea0470bacd1e9c6a0080ab886545019fa0cb3a7109573fad6b61a362844c3a0ac52b36e4bb + languageName: node + linkType: hard + +"deep-eql@npm:^5.0.1": + version: 5.0.2 + resolution: "deep-eql@npm:5.0.2" + checksum: 10c0/7102cf3b7bb719c6b9c0db2e19bf0aa9318d141581befe8c7ce8ccd39af9eaa4346e5e05adef7f9bd7015da0f13a3a25dcfe306ef79dc8668aedbecb658dd247 + languageName: node + linkType: hard + +"define-lazy-prop@npm:^2.0.0": + version: 2.0.0 + resolution: "define-lazy-prop@npm:2.0.0" + checksum: 10c0/db6c63864a9d3b7dc9def55d52764968a5af296de87c1b2cc71d8be8142e445208071953649e0386a8cc37cfcf9a2067a47207f1eb9ff250c2a269658fdae422 + languageName: node + linkType: hard + +"defu@npm:^6.1.6": + version: 6.1.7 + resolution: "defu@npm:6.1.7" + checksum: 10c0/e6635388103c8be3c574ac31302f6930e5e6eeedba32cb1b30cf993c7d9fb571aec2485446dfa23bfa63e55e66156fe109027a9695db82a50f931e91e8d4bedb + languageName: node + linkType: hard + +"depd@npm:~2.0.0": + version: 2.0.0 + resolution: "depd@npm:2.0.0" + checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c + languageName: node + linkType: hard + +"dequal@npm:^2.0.0": + version: 2.0.3 + resolution: "dequal@npm:2.0.3" + checksum: 10c0/f98860cdf58b64991ae10205137c0e97d384c3a4edc7f807603887b7c4b850af1224a33d88012009f150861cbee4fa2d322c4cc04b9313bee312e47f6ecaa888 + languageName: node + linkType: hard + +"destr@npm:^2.0.5": + version: 2.0.5 + resolution: "destr@npm:2.0.5" + checksum: 10c0/efabffe7312a45ad90d79975376be958c50069f1156b94c181199763a7f971e113bd92227c26b94a169c71ca7dbc13583b7e96e5164743969fc79e1ff153e646 + languageName: node + linkType: hard + +"detect-libc@npm:2.0.2": + version: 2.0.2 + resolution: "detect-libc@npm:2.0.2" + checksum: 10c0/a9f4ffcd2701525c589617d98afe5a5d0676c8ea82bcc4ed6f3747241b79f781d36437c59a5e855254c864d36a3e9f8276568b6b531c28d6e53b093a15703f11 + languageName: node + linkType: hard + +"detect-libc@npm:^2.0.3, detect-libc@npm:^2.1.2": + version: 2.1.2 + resolution: "detect-libc@npm:2.1.2" + checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 + languageName: node + linkType: hard + +"detect-node-es@npm:^1.1.0": + version: 1.1.0 + resolution: "detect-node-es@npm:1.1.0" + checksum: 10c0/e562f00de23f10c27d7119e1af0e7388407eb4b06596a25f6d79a360094a109ff285de317f02b090faae093d314cf6e73ac3214f8a5bb3a0def5bece94557fbe + languageName: node + linkType: hard + +"devalue@npm:^5.8.1": + version: 5.9.4 + resolution: "devalue@npm:5.9.4" + checksum: 10c0/d621083acce13d7812418ed07d99f027f83ee00ba591a2d2eab4c9436e1491ebce7bc1a92aa4c1fb7903c31c0e3c0a3cdf8662bf31644742202e003c04c17e33 + languageName: node + linkType: hard + +"devlop@npm:^1.0.0": + version: 1.1.0 + resolution: "devlop@npm:1.1.0" + dependencies: + dequal: "npm:^2.0.0" + checksum: 10c0/e0928ab8f94c59417a2b8389c45c55ce0a02d9ac7fd74ef62d01ba48060129e1d594501b77de01f3eeafc7cb00773819b0df74d96251cf20b31c5b3071f45c0e + languageName: node + linkType: hard + +"diff@npm:^9.0.0": + version: 9.0.0 + resolution: "diff@npm:9.0.0" + checksum: 10c0/a971cc88f66071a33bd3942db2f51b57d484e9856265ae45cf44fb28ab2fde91f132d9c26d8be0fb6082d2be94d29e1295c1adb251b7461935d6a2dd304cd161 + languageName: node + linkType: hard + +"doctrine@npm:^3.0.0": + version: 3.0.0 + resolution: "doctrine@npm:3.0.0" + dependencies: + esutils: "npm:^2.0.2" + checksum: 10c0/c96bdccabe9d62ab6fea9399fdff04a66e6563c1d6fb3a3a063e8d53c3bb136ba63e84250bbf63d00086a769ad53aef92d2bd483f03f837fc97b71cbee6b2520 + languageName: node + linkType: hard + +"dom-accessibility-api@npm:^0.6.3": + version: 0.6.3 + resolution: "dom-accessibility-api@npm:0.6.3" + checksum: 10c0/10bee5aa514b2a9a37c87cd81268db607a2e933a050074abc2f6fa3da9080ebed206a320cbc123567f2c3087d22292853bdfdceaffdd4334ffe2af9510b29360 + languageName: node + linkType: hard + +"dom-serializer@npm:^2.0.0": + version: 2.0.0 + resolution: "dom-serializer@npm:2.0.0" + dependencies: + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.2" + entities: "npm:^4.2.0" + checksum: 10c0/d5ae2b7110ca3746b3643d3ef60ef823f5f078667baf530cec096433f1627ec4b6fa8c072f09d079d7cda915fd2c7bc1b7b935681e9b09e591e1e15f4040b8e2 + languageName: node + linkType: hard + +"domelementtype@npm:^2.3.0": + version: 2.3.0 + resolution: "domelementtype@npm:2.3.0" + checksum: 10c0/686f5a9ef0fff078c1412c05db73a0dce096190036f33e400a07e2a4518e9f56b1e324f5c576a0a747ef0e75b5d985c040b0d51945ce780c0dd3c625a18cd8c9 + languageName: node + linkType: hard + +"domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": + version: 5.0.3 + resolution: "domhandler@npm:5.0.3" + dependencies: + domelementtype: "npm:^2.3.0" + checksum: 10c0/bba1e5932b3e196ad6862286d76adc89a0dbf0c773e5ced1eb01f9af930c50093a084eff14b8de5ea60b895c56a04d5de8bbc4930c5543d029091916770b2d2a + languageName: node + linkType: hard + +"domutils@npm:^3.2.2": + version: 3.2.2 + resolution: "domutils@npm:3.2.2" + dependencies: + dom-serializer: "npm:^2.0.0" + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + checksum: 10c0/47938f473b987ea71cd59e59626eb8666d3aa8feba5266e45527f3b636c7883cca7e582d901531961f742c519d7514636b7973353b648762b2e3bedbf235fada + languageName: node + linkType: hard + +"drizzle-kit@npm:^0.30.2": + version: 0.30.6 + resolution: "drizzle-kit@npm:0.30.6" + dependencies: + "@drizzle-team/brocli": "npm:^0.10.2" + "@esbuild-kit/esm-loader": "npm:^2.5.5" + esbuild: "npm:^0.19.7" + esbuild-register: "npm:^3.5.0" + gel: "npm:^2.0.0" + bin: + drizzle-kit: bin.cjs + checksum: 10c0/a210a8349654b2baf3767fe7b3d4cc90aaae52810437d97f70c4e9f17b37543a0e460f94c643dba23f512d2298772eb6485ecd26599ffcdef9ef5cf90c4885f1 + languageName: node + linkType: hard + +"drizzle-orm@npm:^0.45.3": + version: 0.45.3 + resolution: "drizzle-orm@npm:0.45.3" + peerDependencies: + "@aws-sdk/client-rds-data": ">=3" + "@cloudflare/workers-types": ">=4" + "@electric-sql/pglite": ">=0.2.0" + "@libsql/client": ">=0.10.0" + "@libsql/client-wasm": ">=0.10.0" + "@neondatabase/serverless": ">=0.10.0" + "@netlify/db": ">=0.4.0" + "@op-engineering/op-sqlite": ">=2" + "@opentelemetry/api": ^1.4.1 + "@planetscale/database": ">=1.13" + "@prisma/client": "*" + "@tidbcloud/serverless": "*" + "@types/better-sqlite3": "*" + "@types/pg": "*" + "@types/sql.js": "*" + "@upstash/redis": ">=1.34.7" + "@vercel/postgres": ">=0.8.0" + "@xata.io/client": "*" + better-sqlite3: ">=7" + bun-types: "*" + expo-sqlite: ">=14.0.0" + gel: ">=2" + knex: "*" + kysely: "*" + mysql2: ">=2" + pg: ">=8" + postgres: ">=3" + sql.js: ">=1" + sqlite3: ">=5" + peerDependenciesMeta: + "@aws-sdk/client-rds-data": + optional: true + "@cloudflare/workers-types": + optional: true + "@electric-sql/pglite": + optional: true + "@libsql/client": + optional: true + "@libsql/client-wasm": + optional: true + "@neondatabase/serverless": + optional: true + "@netlify/db": + optional: true + "@op-engineering/op-sqlite": + optional: true + "@opentelemetry/api": + optional: true + "@planetscale/database": + optional: true + "@prisma/client": + optional: true + "@tidbcloud/serverless": + optional: true + "@types/better-sqlite3": + optional: true + "@types/pg": + optional: true + "@types/sql.js": + optional: true + "@upstash/redis": + optional: true + "@vercel/postgres": + optional: true + "@xata.io/client": + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + checksum: 10c0/493919fae4431b3b360227b929fca27b2d220114d7ebd07c2f299efa2a968af52d35f39745daedf9ce2c8c52c6fa875d7a97cb970f144598ed7cd70bc3b1004b + languageName: node + linkType: hard + +"dset@npm:^3.1.4": + version: 3.1.4 + resolution: "dset@npm:3.1.4" + checksum: 10c0/b67bbd28dd8a539e90c15ffb61100eb64ef995c5270a124d4f99bbb53f4d82f55a051b731ba81f3215dd9dce2b4c8d69927dc20b3be1c5fc88bab159467aa438 + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 + languageName: node + linkType: hard + +"ee-first@npm:1.1.1": + version: 1.1.1 + resolution: "ee-first@npm:1.1.1" + checksum: 10c0/b5bb125ee93161bc16bfe6e56c6b04de5ad2aa44234d8f644813cc95d861a6910903132b05093706de2b706599367c4130eb6d170f6b46895686b95f87d017b7 + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.5.427": + version: 1.5.434 + resolution: "electron-to-chromium@npm:1.5.434" + checksum: 10c0/b38aedb459ef044047197d8a3aa2d6013adfc4eb09b8b32f19d89a72b7a2e09774bb050034140968a7fe9f26e7fb394dd8b996c6e501d6c5d8d146fbff935550 + languageName: node + linkType: hard + +"emmet@npm:^2.4.3": + version: 2.4.11 + resolution: "emmet@npm:2.4.11" + dependencies: + "@emmetio/abbreviation": "npm:^2.3.3" + "@emmetio/css-abbreviation": "npm:^2.1.8" + checksum: 10c0/4099d9d0d5dee766603c4ea03e1b87296bd397a0e8c6d8d5d6dcfdaad3e4581df5d48939a00eb4437dc08c83e857e231222ee037cb34ad63b1f2cce4041c6fc2 + languageName: node + linkType: hard + +"emoji-regex@npm:^10.3.0": + version: 10.6.0 + resolution: "emoji-regex@npm:10.6.0" + checksum: 10c0/1e4aa097bb007301c3b4b1913879ae27327fdc48e93eeefefe3b87e495eb33c5af155300be951b4349ff6ac084f4403dc9eff970acba7c1c572d89396a9a32d7 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 + languageName: node + linkType: hard + +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 + languageName: node + linkType: hard + +"encodeurl@npm:^2.0.0": + version: 2.0.0 + resolution: "encodeurl@npm:2.0.0" + checksum: 10c0/5d317306acb13e6590e28e27924c754163946a2480de11865c991a3a7eed4315cd3fba378b543ca145829569eefe9b899f3d84bb09870f675ae60bc924b01ceb + languageName: node + linkType: hard + +"enhanced-resolve@npm:^5.24.1": + version: 5.25.1 + resolution: "enhanced-resolve@npm:5.25.1" + dependencies: + graceful-fs: "npm:^4.2.4" + tapable: "npm:^2.3.3" + checksum: 10c0/c19ac10cd01f3c03648c0bc8fc981810490af6c10506e9ba4a300d57956c8948eeabf0e6af0d3bed91f37b89a775746664d5043c61c7c03a946f1a235013e955 + languageName: node + linkType: hard + +"entities@npm:^4.2.0": + version: 4.5.0 + resolution: "entities@npm:4.5.0" + checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250 + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0, env-paths@npm:^2.2.1": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 + languageName: node + linkType: hard + +"env-paths@npm:^3.0.0": + version: 3.0.0 + resolution: "env-paths@npm:3.0.0" + checksum: 10c0/76dec878cee47f841103bacd7fae03283af16f0702dad65102ef0a556f310b98a377885e0f32943831eb08b5ab37842a323d02529f3dfd5d0a40ca71b01b435f + languageName: node + linkType: hard + +"error-ex@npm:^1.3.1": + version: 1.3.4 + resolution: "error-ex@npm:1.3.4" + dependencies: + is-arrayish: "npm:^0.2.1" + checksum: 10c0/b9e34ff4778b8f3b31a8377e1c654456f4c41aeaa3d10a1138c3b7635d8b7b2e03eb2475d46d8ae055c1f180a1063e100bffabf64ea7e7388b37735df5328664 + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + +"es-module-lexer@npm:^2.0.0": + version: 2.3.2 + resolution: "es-module-lexer@npm:2.3.2" + checksum: 10c0/5e7389424c43478439f12f9a6aca1750f6f99afa384fc3de329f4a45f152ab156671055008adaafc74960877abf8cc338aeebf6bed8c21297b146fd6eb7a22f8 + languageName: node + linkType: hard + +"es-toolkit@npm:^1.46.0": + version: 1.52.0 + resolution: "es-toolkit@npm:1.52.0" + dependenciesMeta: + "@trivago/prettier-plugin-sort-imports@4.3.0": + unplugged: true + prettier-plugin-sort-re-exports@0.0.1: + unplugged: true + vitepress-plugin-sandpack@1.1.4: + unplugged: true + checksum: 10c0/465988c0a6b84b6d1d171287cbbe40c3247a1f94acea210ae14bc66b7d90c93e6839bd6937cbaf2a1d206669f3179eb26141a89d9125969a4b8e58cb7d86a2ca + languageName: node + linkType: hard + +"esbuild-register@npm:^3.5.0": + version: 3.6.0 + resolution: "esbuild-register@npm:3.6.0" + dependencies: + debug: "npm:^4.3.4" + peerDependencies: + esbuild: ">=0.12 <1" + checksum: 10c0/77193b7ca32ba9f81b35ddf3d3d0138efb0b1429d71b39480cfee932e1189dd2e492bd32bf04a4d0bc3adfbc7ec7381ceb5ffd06efe35f3e70904f1f686566d5 + languageName: node + linkType: hard + +"esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0, esbuild@npm:^0.25.0": + version: 0.25.12 + resolution: "esbuild@npm:0.25.12" + dependencies: + "@esbuild/aix-ppc64": "npm:0.25.12" + "@esbuild/android-arm": "npm:0.25.12" + "@esbuild/android-arm64": "npm:0.25.12" + "@esbuild/android-x64": "npm:0.25.12" + "@esbuild/darwin-arm64": "npm:0.25.12" + "@esbuild/darwin-x64": "npm:0.25.12" + "@esbuild/freebsd-arm64": "npm:0.25.12" + "@esbuild/freebsd-x64": "npm:0.25.12" + "@esbuild/linux-arm": "npm:0.25.12" + "@esbuild/linux-arm64": "npm:0.25.12" + "@esbuild/linux-ia32": "npm:0.25.12" + "@esbuild/linux-loong64": "npm:0.25.12" + "@esbuild/linux-mips64el": "npm:0.25.12" + "@esbuild/linux-ppc64": "npm:0.25.12" + "@esbuild/linux-riscv64": "npm:0.25.12" + "@esbuild/linux-s390x": "npm:0.25.12" + "@esbuild/linux-x64": "npm:0.25.12" + "@esbuild/netbsd-arm64": "npm:0.25.12" + "@esbuild/netbsd-x64": "npm:0.25.12" + "@esbuild/openbsd-arm64": "npm:0.25.12" + "@esbuild/openbsd-x64": "npm:0.25.12" + "@esbuild/openharmony-arm64": "npm:0.25.12" + "@esbuild/sunos-x64": "npm:0.25.12" + "@esbuild/win32-arm64": "npm:0.25.12" + "@esbuild/win32-ia32": "npm:0.25.12" + "@esbuild/win32-x64": "npm:0.25.12" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/c205357531423220a9de8e1e6c6514242bc9b1666e762cd67ccdf8fdfdc3f1d0bd76f8d9383958b97ad4c953efdb7b6e8c1f9ca5951cd2b7c5235e8755b34a6b + languageName: node + linkType: hard + +"esbuild@npm:^0.19.7": + version: 0.19.12 + resolution: "esbuild@npm:0.19.12" + dependencies: + "@esbuild/aix-ppc64": "npm:0.19.12" + "@esbuild/android-arm": "npm:0.19.12" + "@esbuild/android-arm64": "npm:0.19.12" + "@esbuild/android-x64": "npm:0.19.12" + "@esbuild/darwin-arm64": "npm:0.19.12" + "@esbuild/darwin-x64": "npm:0.19.12" + "@esbuild/freebsd-arm64": "npm:0.19.12" + "@esbuild/freebsd-x64": "npm:0.19.12" + "@esbuild/linux-arm": "npm:0.19.12" + "@esbuild/linux-arm64": "npm:0.19.12" + "@esbuild/linux-ia32": "npm:0.19.12" + "@esbuild/linux-loong64": "npm:0.19.12" + "@esbuild/linux-mips64el": "npm:0.19.12" + "@esbuild/linux-ppc64": "npm:0.19.12" + "@esbuild/linux-riscv64": "npm:0.19.12" + "@esbuild/linux-s390x": "npm:0.19.12" + "@esbuild/linux-x64": "npm:0.19.12" + "@esbuild/netbsd-x64": "npm:0.19.12" + "@esbuild/openbsd-x64": "npm:0.19.12" + "@esbuild/sunos-x64": "npm:0.19.12" + "@esbuild/win32-arm64": "npm:0.19.12" + "@esbuild/win32-ia32": "npm:0.19.12" + "@esbuild/win32-x64": "npm:0.19.12" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/0f2d21ffe24ebead64843f87c3aebe2e703a5ed9feb086a0728b24907fac2eb9923e4a79857d3df9059c915739bd7a870dd667972eae325c67f478b592b8582d + languageName: node + linkType: hard + +"esbuild@npm:^0.27.0 || ^0.28.0, esbuild@npm:^0.28.0, esbuild@npm:~0.28.0": + version: 0.28.2 + resolution: "esbuild@npm:0.28.2" + dependencies: + "@esbuild/aix-ppc64": "npm:0.28.2" + "@esbuild/android-arm": "npm:0.28.2" + "@esbuild/android-arm64": "npm:0.28.2" + "@esbuild/android-x64": "npm:0.28.2" + "@esbuild/darwin-arm64": "npm:0.28.2" + "@esbuild/darwin-x64": "npm:0.28.2" + "@esbuild/freebsd-arm64": "npm:0.28.2" + "@esbuild/freebsd-x64": "npm:0.28.2" + "@esbuild/linux-arm": "npm:0.28.2" + "@esbuild/linux-arm64": "npm:0.28.2" + "@esbuild/linux-ia32": "npm:0.28.2" + "@esbuild/linux-loong64": "npm:0.28.2" + "@esbuild/linux-mips64el": "npm:0.28.2" + "@esbuild/linux-ppc64": "npm:0.28.2" + "@esbuild/linux-riscv64": "npm:0.28.2" + "@esbuild/linux-s390x": "npm:0.28.2" + "@esbuild/linux-x64": "npm:0.28.2" + "@esbuild/netbsd-arm64": "npm:0.28.2" + "@esbuild/netbsd-x64": "npm:0.28.2" + "@esbuild/openbsd-arm64": "npm:0.28.2" + "@esbuild/openbsd-x64": "npm:0.28.2" + "@esbuild/openharmony-arm64": "npm:0.28.2" + "@esbuild/sunos-x64": "npm:0.28.2" + "@esbuild/win32-arm64": "npm:0.28.2" + "@esbuild/win32-ia32": "npm:0.28.2" + "@esbuild/win32-x64": "npm:0.28.2" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/9b19edb63bd7780fd2e8e65a1394a0e8a05a17d697f73f0647ffe3c134709d8dbe744ab3fd57b35c9470db268cae7678fafd3dcd3ce1f34fc590568c2433f5d2 + languageName: node + linkType: hard + +"esbuild@npm:~0.18.20": + version: 0.18.20 + resolution: "esbuild@npm:0.18.20" + dependencies: + "@esbuild/android-arm": "npm:0.18.20" + "@esbuild/android-arm64": "npm:0.18.20" + "@esbuild/android-x64": "npm:0.18.20" + "@esbuild/darwin-arm64": "npm:0.18.20" + "@esbuild/darwin-x64": "npm:0.18.20" + "@esbuild/freebsd-arm64": "npm:0.18.20" + "@esbuild/freebsd-x64": "npm:0.18.20" + "@esbuild/linux-arm": "npm:0.18.20" + "@esbuild/linux-arm64": "npm:0.18.20" + "@esbuild/linux-ia32": "npm:0.18.20" + "@esbuild/linux-loong64": "npm:0.18.20" + "@esbuild/linux-mips64el": "npm:0.18.20" + "@esbuild/linux-ppc64": "npm:0.18.20" + "@esbuild/linux-riscv64": "npm:0.18.20" + "@esbuild/linux-s390x": "npm:0.18.20" + "@esbuild/linux-x64": "npm:0.18.20" + "@esbuild/netbsd-x64": "npm:0.18.20" + "@esbuild/openbsd-x64": "npm:0.18.20" + "@esbuild/sunos-x64": "npm:0.18.20" + "@esbuild/win32-arm64": "npm:0.18.20" + "@esbuild/win32-ia32": "npm:0.18.20" + "@esbuild/win32-x64": "npm:0.18.20" + dependenciesMeta: + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/473b1d92842f50a303cf948a11ebd5f69581cd254d599dd9d62f9989858e0533f64e83b723b5e1398a5b488c0f5fd088795b4235f65ecaf4f007d4b79f04bc88 + languageName: node + linkType: hard + +"escalade@npm:^3.1.1, escalade@npm:^3.2.0": + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 + languageName: node + linkType: hard + +"escape-html@npm:^1.0.3": + version: 1.0.3 + resolution: "escape-html@npm:1.0.3" + checksum: 10c0/524c739d776b36c3d29fa08a22e03e8824e3b2fd57500e5e44ecf3cc4707c34c60f9ca0781c0e33d191f2991161504c295e98f68c78fe7baa6e57081ec6ac0a3 + languageName: node + linkType: hard + +"esprima@npm:~4.0.0": + version: 4.0.1 + resolution: "esprima@npm:4.0.1" + bin: + esparse: ./bin/esparse.js + esvalidate: ./bin/esvalidate.js + checksum: 10c0/ad4bab9ead0808cf56501750fd9d3fb276f6b105f987707d059005d57e182d18a7c9ec7f3a01794ebddcca676773e42ca48a32d67a250c9d35e009ca613caba3 + languageName: node + linkType: hard + +"estree-walker@npm:^2.0.2": + version: 2.0.2 + resolution: "estree-walker@npm:2.0.2" + checksum: 10c0/53a6c54e2019b8c914dc395890153ffdc2322781acf4bd7d1a32d7aedc1710807bdcd866ac133903d5629ec601fbb50abe8c2e5553c7f5a0afdd9b6af6c945af + languageName: node + linkType: hard + +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + +"esutils@npm:^2.0.2": + version: 2.0.3 + resolution: "esutils@npm:2.0.3" + checksum: 10c0/9a2fe69a41bfdade834ba7c42de4723c97ec776e40656919c62cbd13607c45e127a003f05f724a1ea55e5029a4cf2de444b13009f2af71271e42d93a637137c7 + languageName: node + linkType: hard + +"etag@npm:^1.8.1": + version: 1.8.1 + resolution: "etag@npm:1.8.1" + checksum: 10c0/12be11ef62fb9817314d790089a0a49fae4e1b50594135dcb8076312b7d7e470884b5100d249b28c18581b7fd52f8b485689ffae22a11ed9ec17377a33a08f84 + languageName: node + linkType: hard + +"eventemitter3@npm:^5.0.4": + version: 5.0.4 + resolution: "eventemitter3@npm:5.0.4" + checksum: 10c0/575b8cac8d709e1473da46f8f15ef311b57ff7609445a7c71af5cd42598583eee6f098fa7a593e30f27e94b8865642baa0689e8fa97c016f742abdb3b1bf6d9a + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.3 + resolution: "exponential-backoff@npm:3.1.3" + checksum: 10c0/77e3ae682b7b1f4972f563c6dbcd2b0d54ac679e62d5d32f3e5085feba20483cf28bd505543f520e287a56d4d55a28d7874299941faf637e779a1aa5994d1267 + languageName: node + linkType: hard + +"extend@npm:^3.0.0": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: 10c0/73bf6e27406e80aa3e85b0d1c4fd987261e628064e170ca781125c0b635a3dabad5e05adbf07595ea0cf1e6c5396cacb214af933da7cbaf24fe75ff14818e8f9 + languageName: node + linkType: hard + +"fast-deep-equal@npm:^3.1.3": + version: 3.1.3 + resolution: "fast-deep-equal@npm:3.1.3" + checksum: 10c0/40dedc862eb8992c54579c66d914635afbec43350afbbe991235fdcb4e3a8d5af1b23ae7e79bef7d4882d0ecee06c3197488026998fb19f72dc95acff1d1b1d0 + languageName: node + linkType: hard + +"fast-string-truncated-width@npm:^3.0.2": + version: 3.0.3 + resolution: "fast-string-truncated-width@npm:3.0.3" + checksum: 10c0/043b8663397d14a3880ce4f3407bcda60b40db9bbeafe62863a35d1f9c69ea17c8da3fcd72de235553e6c9cd053128cde9e24ca0d4a7463208f48db3cd23d981 + languageName: node + linkType: hard + +"fast-string-width@npm:^3.0.2": + version: 3.0.2 + resolution: "fast-string-width@npm:3.0.2" + dependencies: + fast-string-truncated-width: "npm:^3.0.2" + checksum: 10c0/c8822d175315bb353ebe782b65214ac53b13e3bf704e03b132ea7bdfa8de6a636375b3ab7a4097545393d109381c37c4f387c72a462c90b61412dbc4632f39a7 + languageName: node + linkType: hard + +"fast-uri@npm:^3.0.1": + version: 3.1.8 + resolution: "fast-uri@npm:3.1.8" + checksum: 10c0/c9c47860a7f9110176d94bb30a02942182f0bfc8d6780e9702f320336dc2dadb83b1d9879cceaf8d4b439642d1ec6d78c317cb808ab155455551bd5d9d7c22d8 + languageName: node + linkType: hard + +"fast-wrap-ansi@npm:^0.2.0": + version: 0.2.2 + resolution: "fast-wrap-ansi@npm:0.2.2" + dependencies: + fast-string-width: "npm:^3.0.2" + checksum: 10c0/1aa7be4f7cb86f4bdb14691cb6bcc0b8df8b3b89df142ade3ae1602332dcf6f990cd750a923cd581ca0847808cb4ec1aa5afaafa7a72f849e87a2a62c98fa370 + languageName: node + linkType: hard + +"fdir@npm:^6.4.4, fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f + languageName: node + linkType: hard + +"fetch-blob@npm:^3.1.2, fetch-blob@npm:^3.1.4": + version: 3.2.0 + resolution: "fetch-blob@npm:3.2.0" + dependencies: + node-domexception: "npm:^1.0.0" + web-streams-polyfill: "npm:^3.0.3" + checksum: 10c0/60054bf47bfa10fb0ba6cb7742acec2f37c1f56344f79a70bb8b1c48d77675927c720ff3191fa546410a0442c998d27ab05e9144c32d530d8a52fbe68f843b69 + languageName: node + linkType: hard + +"find-proc@npm:0.2.0": + version: 0.2.0 + resolution: "find-proc@npm:0.2.0" + checksum: 10c0/c8a704e8d4b25f096654a1fc3eaf1434111a4fae23069b6c12c941d812675b203b5c707674fe58257f7833e010db047bd09fdf402e56d78003dcf4c939121470 + languageName: node + linkType: hard + +"find-up@npm:^7.0.0": + version: 7.0.0 + resolution: "find-up@npm:7.0.0" + dependencies: + locate-path: "npm:^7.2.0" + path-exists: "npm:^5.0.0" + unicorn-magic: "npm:^0.1.0" + checksum: 10c0/e6ee3e6154560bc0ab3bc3b7d1348b31513f9bdf49a5dd2e952495427d559fa48cdf33953e85a309a323898b43fa1bfbc8b80c880dfc16068384783034030008 + languageName: node + linkType: hard + +"flattie@npm:^1.1.1": + version: 1.1.1 + resolution: "flattie@npm:1.1.1" + checksum: 10c0/a8f8242c7af126cb2f1aa4a067af338fce609fc4c4df183c626fcc70a46c1878ce4aa88cd0dc8ef8f583ad4e7088a3b11ebeb6a62c9c97d75c0b1b0f08182ee3 + languageName: node + linkType: hard + +"fontace@npm:~0.4.1": + version: 0.4.1 + resolution: "fontace@npm:0.4.1" + dependencies: + fontkitten: "npm:^1.0.2" + checksum: 10c0/2b3b8bc60192b0e9d87c369f1d72bf5da6c5b0f6055d5eeb0c06693455c55395f9592844273035399289f410202306c1d7f7329312e6e42a2694912d39c70709 + languageName: node + linkType: hard + +"fontkitten@npm:^1.0.2, fontkitten@npm:^1.0.3": + version: 1.0.3 + resolution: "fontkitten@npm:1.0.3" + dependencies: + tiny-inflate: "npm:^1.0.3" + checksum: 10c0/edee7bde2f824a778cf401a2019ada63376fc8bcc5303d09a11e8a9245f35bd945934abea04aef1ca52e8dbb6ce3a17fe9ecdfa52cc0c92f56b25709fb865a76 + languageName: node + linkType: hard + +"foreground-child@npm:^3.1.0": + version: 3.3.1 + resolution: "foreground-child@npm:3.3.1" + dependencies: + cross-spawn: "npm:^7.0.6" + signal-exit: "npm:^4.0.1" + checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3 + languageName: node + linkType: hard + +"formdata-polyfill@npm:^4.0.10": + version: 4.0.10 + resolution: "formdata-polyfill@npm:4.0.10" + dependencies: + fetch-blob: "npm:^3.1.2" + checksum: 10c0/5392ec484f9ce0d5e0d52fb5a78e7486637d516179b0eb84d81389d7eccf9ca2f663079da56f761355c0a65792810e3b345dc24db9a8bbbcf24ef3c8c88570c6 + languageName: node + linkType: hard + +"fresh@npm:^2.0.0": + version: 2.0.0 + resolution: "fresh@npm:2.0.0" + checksum: 10c0/0557548194cb9a809a435bf92bcfbc20c89e8b5eb38861b73ced36750437251e39a111fc3a18b98531be9dd91fe1411e4969f229dc579ec0251ce6c5d4900bbc + languageName: node + linkType: hard + +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60 + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin<compat/fsevents>, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin<compat/fsevents>": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin<compat/fsevents>::version=2.3.3&hash=df0bf1" + dependencies: + node-gyp: "npm:latest" + conditions: os=darwin + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 + languageName: node + linkType: hard + +"gel@npm:^2.0.0": + version: 2.2.1 + resolution: "gel@npm:2.2.1" + dependencies: + "@petamoriken/float16": "npm:^3.8.7" + debug: "npm:^4.3.4" + env-paths: "npm:^3.0.0" + semver: "npm:^7.6.2" + shell-quote: "npm:^1.8.1" + which: "npm:^4.0.0" + bin: + gel: dist/cli.mjs + checksum: 10c0/2fcf1dfa994dadce2f688f8c500eb4585bcc6c6a07270249c6680f66824eb801e1e4cda44a3b84a7add582eddb6d4dc74cc9704cca31a14ba1e3bcea397ec89a + languageName: node + linkType: hard + +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: 10c0/782aba6cba65b1bb5af3b095d96249d20edbe8df32dbf4696fd49be2583faf676173bf4809386588828e4dd76a3354fcbeb577bab1c833ccd9fc4577f26103f8 + languageName: node + linkType: hard + +"get-caller-file@npm:^2.0.5": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde + languageName: node + linkType: hard + +"get-east-asian-width@npm:^1.0.0, get-east-asian-width@npm:^1.5.0": + version: 1.7.0 + resolution: "get-east-asian-width@npm:1.7.0" + checksum: 10c0/680a5f611a186e4a065291f31821f7e4ad7742625be814e05b8ab5535c6fa53562bc0333a83138fca5f2e1030ed960a561b2fd4629e0cff0dc68b19cf46d7c53 + languageName: node + linkType: hard + +"get-nonce@npm:^1.0.0": + version: 1.0.1 + resolution: "get-nonce@npm:1.0.1" + checksum: 10c0/2d7df55279060bf0568549e1ffc9b84bc32a32b7541675ca092dce56317cdd1a59a98dcc4072c9f6a980779440139a3221d7486f52c488e69dc0fd27b1efb162 + languageName: node + linkType: hard + +"get-tsconfig@npm:5.0.0-beta.4": + version: 5.0.0-beta.4 + resolution: "get-tsconfig@npm:5.0.0-beta.4" + dependencies: + resolve-pkg-maps: "npm:^1.0.0" + checksum: 10c0/6b908fc5d2abc937fca6938f5e9218238f49b11cd07362831383aa5815b25ce6f5fff36d4bcd70caa17778a54922b654745ce6c0b1bb19be8eac87da4e3fc087 + languageName: node + linkType: hard + +"get-tsconfig@npm:^4.7.0": + version: 4.14.3 + resolution: "get-tsconfig@npm:4.14.3" + dependencies: + resolve-pkg-maps: "npm:^1.0.0" + checksum: 10c0/22119bb5b7e37b922a973bfb50deac89ca845cef50a24cd27b6c628f314e91a2bccceb186e0b5183e231ad6480d7173738912975332caf60678af39b7236eeac + languageName: node + linkType: hard + +"github-slugger@npm:^2.0.0": + version: 2.0.0 + resolution: "github-slugger@npm:2.0.0" + checksum: 10c0/21b912b6b1e48f1e5a50b2292b48df0ff6abeeb0691b161b3d93d84f4ae6b1acd6ae23702e914af7ea5d441c096453cf0f621b72d57893946618d21dd1a1c486 + languageName: node + linkType: hard + +"glob@npm:^10.0.0": + version: 10.5.0 + resolution: "glob@npm:10.5.0" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/100705eddbde6323e7b35e1d1ac28bcb58322095bd8e63a7d0bef1a2cdafe0d0f7922a981b2b48369a4f8c1b077be5c171804534c3509dfe950dde15fbe6d828 + languageName: node + linkType: hard + +"global-directory@npm:^5.0.0": + version: 5.0.0 + resolution: "global-directory@npm:5.0.0" + dependencies: + ini: "npm:6.0.0" + checksum: 10c0/2b90eea8975bb332db7e8c9096ff310f0deb617d17e47e93b921d1c69f7677c1759a07009f2581f050d3ea08a3e079bf4ffdb9c34c94d48dc369c6577b3d45f4 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + +"gsap@npm:^3.15.0": + version: 3.15.0 + resolution: "gsap@npm:3.15.0" + checksum: 10c0/5d86de72cb0bd6b20502c34c329a83a7d679979731bb465164407b7a5225aa61d612beee740eea36258622897c116cd2ca35320d530b7cf68bad62522361fcf7 + languageName: node + linkType: hard + +"h3@npm:^1.15.10": + version: 1.15.11 + resolution: "h3@npm:1.15.11" + dependencies: + cookie-es: "npm:^1.2.3" + crossws: "npm:^0.3.5" + defu: "npm:^6.1.6" + destr: "npm:^2.0.5" + iron-webcrypto: "npm:^1.2.1" + node-mock-http: "npm:^1.0.4" + radix3: "npm:^1.1.2" + ufo: "npm:^1.6.3" + uncrypto: "npm:^0.1.3" + checksum: 10c0/6ccb421b9f92e02e6330c2b6697b18ef18e9550e4a1708f224ca517c40ecd201cd00967d0feb26e718595e86e985edec1755933cf8792d34fb8504f1c7cc261d + languageName: node + linkType: hard + +"hasown@npm:^2.0.4": + version: 2.0.4 + resolution: "hasown@npm:2.0.4" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/2d8de939e270b70618f8cebb69746620db10617dbb495bc66ddad326955ea24d3ca4af133aff3eb7c1853e0218f867bc2b050ec26fe02e3aea58f880ffc5e506 + languageName: node + linkType: hard + +"hast-util-to-html@npm:^9.0.5": + version: 9.0.5 + resolution: "hast-util-to-html@npm:9.0.5" + dependencies: + "@types/hast": "npm:^3.0.0" + "@types/unist": "npm:^3.0.0" + ccount: "npm:^2.0.0" + comma-separated-tokens: "npm:^2.0.0" + hast-util-whitespace: "npm:^3.0.0" + html-void-elements: "npm:^3.0.0" + mdast-util-to-hast: "npm:^13.0.0" + property-information: "npm:^7.0.0" + space-separated-tokens: "npm:^2.0.0" + stringify-entities: "npm:^4.0.0" + zwitch: "npm:^2.0.4" + checksum: 10c0/b7a08c30bab4371fc9b4a620965c40b270e5ae7a8e94cf885f43b21705179e28c8e43b39c72885d1647965fb3738654e6962eb8b58b0c2a84271655b4d748836 + languageName: node + linkType: hard + +"hast-util-whitespace@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-whitespace@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 10c0/b898bc9fe27884b272580d15260b6bbdabe239973a147e97fa98c45fa0ffec967a481aaa42291ec34fb56530dc2d484d473d7e2bae79f39c83f3762307edfea8 + languageName: node + linkType: hard + +"hookable@npm:^6.1.1": + version: 6.1.2 + resolution: "hookable@npm:6.1.2" + checksum: 10c0/ea5cdb1b1a1919b48cfbbfb55c8009f6843473ff50f67d6e959fd291cb2fd0a4934e85124bfd6531b1d2edf1e0675bc5954f85a12318a59b15c7a3e5667f0d4e + languageName: node + linkType: hard + +"html-escaper@npm:3.0.3": + version: 3.0.3 + resolution: "html-escaper@npm:3.0.3" + checksum: 10c0/a042fa4139127ff7546513e90ea39cc9161a1938ce90122dbc4260d4b7252c9aa8452f4509c0c2889901b8ae9a8699179150f1f99d3f80bcf7317573c5f08f4e + languageName: node + linkType: hard + +"html-void-elements@npm:^3.0.0": + version: 3.0.0 + resolution: "html-void-elements@npm:3.0.0" + checksum: 10c0/a8b9ec5db23b7c8053876dad73a0336183e6162bf6d2677376d8b38d654fdc59ba74fdd12f8812688f7db6fad451210c91b300e472afc0909224e0a44c8610d2 + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.2.0": + version: 4.2.0 + resolution: "http-cache-semantics@npm:4.2.0" + checksum: 10c0/45b66a945cf13ec2d1f29432277201313babf4a01d9e52f44b31ca923434083afeca03f18417f599c9ab3d0e7b618ceb21257542338b57c54b710463b4a53e37 + languageName: node + linkType: hard + +"http-errors@npm:^2.0.1": + version: 2.0.1 + resolution: "http-errors@npm:2.0.1" + dependencies: + depd: "npm:~2.0.0" + inherits: "npm:~2.0.4" + setprototypeof: "npm:~1.2.0" + statuses: "npm:~2.0.2" + toidentifier: "npm:~1.0.1" + checksum: 10c0/fb38906cef4f5c83952d97661fe14dc156cb59fe54812a42cd448fa57b5c5dfcb38a40a916957737bd6b87aab257c0648d63eb5b6a9ca9f548e105b6072712d4 + languageName: node + linkType: hard + +"import-fresh@npm:^3.3.0": + version: 3.3.1 + resolution: "import-fresh@npm:3.3.1" + dependencies: + parent-module: "npm:^1.0.0" + resolve-from: "npm:^4.0.0" + checksum: 10c0/bf8cc494872fef783249709385ae883b447e3eb09db0ebd15dcead7d9afe7224dad7bd7591c6b73b0b19b3c0f9640eb8ee884f01cfaf2887ab995b0b36a0cbec + languageName: node + linkType: hard + +"indent-string@npm:^4.0.0": + version: 4.0.0 + resolution: "indent-string@npm:4.0.0" + checksum: 10c0/1e1904ddb0cb3d6cce7cd09e27a90184908b7a5d5c21b92e232c93579d314f0b83c246ffb035493d0504b1e9147ba2c9b21df0030f48673fba0496ecd698161f + languageName: node + linkType: hard + +"inherits@npm:~2.0.4": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 + languageName: node + linkType: hard + +"ini@npm:6.0.0": + version: 6.0.0 + resolution: "ini@npm:6.0.0" + checksum: 10c0/9a7f55f306e2b25b41ae67c8b526e8f4673f057b70852b9025816ef4f15f07bf1ba35ed68ea4471ff7b31718f7ef1bc50d709f8d03cb012e10a3135eb99c7206 + languageName: node + linkType: hard + +"iron-webcrypto@npm:^1.2.1": + version: 1.2.1 + resolution: "iron-webcrypto@npm:1.2.1" + checksum: 10c0/5cf27c6e2bd3ef3b4970e486235fd82491ab8229e2ed0ac23307c28d6c80d721772a86ed4e9fe2a5cabadd710c2f024b706843b40561fb83f15afee58f809f66 + languageName: node + linkType: hard + +"is-arrayish@npm:^0.2.1": + version: 0.2.1 + resolution: "is-arrayish@npm:0.2.1" + checksum: 10c0/e7fb686a739068bb70f860b39b67afc62acc62e36bb61c5f965768abce1873b379c563e61dd2adad96ebb7edf6651111b385e490cf508378959b0ed4cac4e729 + languageName: node + linkType: hard + +"is-core-module@npm:^2.16.1": + version: 2.17.0 + resolution: "is-core-module@npm:2.17.0" + dependencies: + hasown: "npm:^2.0.4" + checksum: 10c0/3dc7c15ea42f846180cd2cdd57af62270e0a8d0ed31c17c85735d554089d39975e2e82e65fc7c8280a3c476ca3d0d09992d0860b4998abb769a277341feb6698 + languageName: node + linkType: hard + +"is-docker@npm:^2.0.0, is-docker@npm:^2.1.1": + version: 2.2.1 + resolution: "is-docker@npm:2.2.1" + bin: + is-docker: cli.js + checksum: 10c0/e828365958d155f90c409cdbe958f64051d99e8aedc2c8c4cd7c89dcf35329daed42f7b99346f7828df013e27deb8f721cf9408ba878c76eb9e8290235fbcdcc + languageName: node + linkType: hard + +"is-docker@npm:^4.0.0": + version: 4.0.0 + resolution: "is-docker@npm:4.0.0" + bin: + is-docker: cli.js + checksum: 10c0/4ee05c305b545422b172cab17f42900b940a5fa77820380d3244784fde69279d6881305f249b3b7fa2e261c2294804100548f5638880842e5b43df72cec29087 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc + languageName: node + linkType: hard + +"is-plain-obj@npm:^4.0.0, is-plain-obj@npm:^4.1.0": + version: 4.1.0 + resolution: "is-plain-obj@npm:4.1.0" + checksum: 10c0/32130d651d71d9564dc88ba7e6fda0e91a1010a3694648e9f4f47bb6080438140696d3e3e15c741411d712e47ac9edc1a8a9de1fe76f3487b0d90be06ac9975e + languageName: node + linkType: hard + +"is-wsl@npm:^2.2.0": + version: 2.2.0 + resolution: "is-wsl@npm:2.2.0" + dependencies: + is-docker: "npm:^2.0.0" + checksum: 10c0/a6fa2d370d21be487c0165c7a440d567274fbba1a817f2f0bfa41cc5e3af25041d84267baa22df66696956038a43973e72fca117918c91431920bdef490fa25e + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d + languageName: node + linkType: hard + +"isexe@npm:^3.1.1": + version: 3.1.5 + resolution: "isexe@npm:3.1.5" + checksum: 10c0/8be2973a09f2f804ea1f34bfccfd5ea219ef48083bdb12107fe5bcf96b3e36b85084409e1b09ddaf2fae8927fdd9f6d70d90baadb78caa1ca7c530935706c8a4 + languageName: node + linkType: hard + +"isexe@npm:^4.0.0": + version: 4.0.0 + resolution: "isexe@npm:4.0.0" + checksum: 10c0/5884815115bceac452877659a9c7726382531592f43dc29e5d48b7c4100661aed54018cb90bd36cb2eaeba521092570769167acbb95c18d39afdccbcca06c5ce + languageName: node + linkType: hard + +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 + languageName: node + linkType: hard + +"jiti@npm:2.6.1": + version: 2.6.1 + resolution: "jiti@npm:2.6.1" + bin: + jiti: lib/jiti-cli.mjs + checksum: 10c0/79b2e96a8e623f66c1b703b98ec1b8be4500e1d217e09b09e343471bbb9c105381b83edbb979d01cef18318cc45ce6e153571b6c83122170eefa531c64b6789b + languageName: node + linkType: hard + +"jiti@npm:^2.7.0": + version: 2.7.0 + resolution: "jiti@npm:2.7.0" + bin: + jiti: lib/jiti-cli.mjs + checksum: 10c0/1b1e2310a490dce1aeea3da5f5dfe18273516c20ce48be2e98eb8ea452d5f3dcc8fd0cfd6d28b4052a24c5dbab6e3089b2d7e79f0bce7915b10d750929563c42 + languageName: node + linkType: hard + +"js-base64@npm:^3.7.5": + version: 3.9.4 + resolution: "js-base64@npm:3.9.4" + checksum: 10c0/c1a76236bb2531fa321863b87eceabb90f070ef196f3a97028d6f710c941bdfc453eb8c75bc2412453db163fe4ac5c748a40a63547a264420ce0e67b871b228d + languageName: node + linkType: hard + +"js-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed + languageName: node + linkType: hard + +"js-yaml@npm:^4.1.0, js-yaml@npm:^4.3.0": + version: 4.3.2 + resolution: "js-yaml@npm:4.3.2" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/dedd34c2e8fef1d504687f1bc94ed0ee1311a1f78770fa2800352c6d59c635ccf555009d74e9afe529ae62199da2b1ccef30e53dc84dcd8d2d8187c22574bc97 + languageName: node + linkType: hard + +"jsesc@npm:^3.0.2": + version: 3.1.0 + resolution: "jsesc@npm:3.1.0" + bin: + jsesc: bin/jsesc + checksum: 10c0/531779df5ec94f47e462da26b4cbf05eb88a83d9f08aac2ba04206508fc598527a153d08bd462bae82fc78b3eaa1a908e1a4a79f886e9238641c4cdefaf118b1 + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^2.3.0": + version: 2.3.1 + resolution: "json-parse-even-better-errors@npm:2.3.1" + checksum: 10c0/140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3 + languageName: node + linkType: hard + +"json-schema-traverse@npm:^1.0.0": + version: 1.0.0 + resolution: "json-schema-traverse@npm:1.0.0" + checksum: 10c0/71e30015d7f3d6dc1c316d6298047c8ef98a06d31ad064919976583eb61e1018a60a0067338f0f79cabc00d84af3fcc489bd48ce8a46ea165d9541ba17fb30c6 + languageName: node + linkType: hard + +"json5@npm:^2.2.2, json5@npm:^2.2.3": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 10c0/5a04eed94810fa55c5ea138b2f7a5c12b97c3750bc63d11e511dcecbfef758003861522a070c2272764ee0f4e3e323862f386945aeb5b85b87ee43f084ba586c + languageName: node + linkType: hard + +"jsonc-parser@npm:^2.3.0": + version: 2.3.1 + resolution: "jsonc-parser@npm:2.3.1" + checksum: 10c0/b5e823612f6518a4d35e65d3c642e87b994c52a71b6d83d306d59f9b57003a1f6c64659808f0f1c3448991c28916d56faca45222f31ddb1a32effecdef0f0485 + languageName: node + linkType: hard + +"jsonc-parser@npm:^3.0.0, jsonc-parser@npm:^3.3.1": + version: 3.3.1 + resolution: "jsonc-parser@npm:3.3.1" + checksum: 10c0/269c3ae0a0e4f907a914bf334306c384aabb9929bd8c99f909275ebd5c2d3bc70b9bcd119ad794f339dec9f24b6a4ee9cd5a8ab2e6435e730ad4075388fc2ab6 + languageName: node + linkType: hard + +"kleur@npm:^4.1.5": + version: 4.1.5 + resolution: "kleur@npm:4.1.5" + checksum: 10c0/e9de6cb49657b6fa70ba2d1448fd3d691a5c4370d8f7bbf1c2f64c24d461270f2117e1b0afe8cb3114f13bbd8e51de158c2a224953960331904e636a5e4c0f2a + languageName: node + linkType: hard + +"lefthook-darwin-arm64@npm:2.1.14": + version: 2.1.14 + resolution: "lefthook-darwin-arm64@npm:2.1.14" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"lefthook-darwin-x64@npm:2.1.14": + version: 2.1.14 + resolution: "lefthook-darwin-x64@npm:2.1.14" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"lefthook-freebsd-arm64@npm:2.1.14": + version: 2.1.14 + resolution: "lefthook-freebsd-arm64@npm:2.1.14" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"lefthook-freebsd-x64@npm:2.1.14": + version: 2.1.14 + resolution: "lefthook-freebsd-x64@npm:2.1.14" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"lefthook-linux-arm64@npm:2.1.14": + version: 2.1.14 + resolution: "lefthook-linux-arm64@npm:2.1.14" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"lefthook-linux-x64@npm:2.1.14": + version: 2.1.14 + resolution: "lefthook-linux-x64@npm:2.1.14" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"lefthook-openbsd-arm64@npm:2.1.14": + version: 2.1.14 + resolution: "lefthook-openbsd-arm64@npm:2.1.14" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"lefthook-openbsd-x64@npm:2.1.14": + version: 2.1.14 + resolution: "lefthook-openbsd-x64@npm:2.1.14" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"lefthook-windows-arm64@npm:2.1.14": + version: 2.1.14 + resolution: "lefthook-windows-arm64@npm:2.1.14" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"lefthook-windows-x64@npm:2.1.14": + version: 2.1.14 + resolution: "lefthook-windows-x64@npm:2.1.14" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"lefthook@npm:^2.1.14": + version: 2.1.14 + resolution: "lefthook@npm:2.1.14" + dependencies: + lefthook-darwin-arm64: "npm:2.1.14" + lefthook-darwin-x64: "npm:2.1.14" + lefthook-freebsd-arm64: "npm:2.1.14" + lefthook-freebsd-x64: "npm:2.1.14" + lefthook-linux-arm64: "npm:2.1.14" + lefthook-linux-x64: "npm:2.1.14" + lefthook-openbsd-arm64: "npm:2.1.14" + lefthook-openbsd-x64: "npm:2.1.14" + lefthook-windows-arm64: "npm:2.1.14" + lefthook-windows-x64: "npm:2.1.14" + dependenciesMeta: + lefthook-darwin-arm64: + optional: true + lefthook-darwin-x64: + optional: true + lefthook-freebsd-arm64: + optional: true + lefthook-freebsd-x64: + optional: true + lefthook-linux-arm64: + optional: true + lefthook-linux-x64: + optional: true + lefthook-openbsd-arm64: + optional: true + lefthook-openbsd-x64: + optional: true + lefthook-windows-arm64: + optional: true + lefthook-windows-x64: + optional: true + bin: + lefthook: bin/index.js + checksum: 10c0/383ea7cabe445f6130ddc2ead36b966a32d0ffb0db37c2e4baf36fd3622b673c61e7fa1c4ab8ff2f074b37eb5eb9efab95ea1be37913ff82e51d987887311da8 + languageName: node + linkType: hard + +"lenis@npm:^1.3.26": + version: 1.3.26 + resolution: "lenis@npm:1.3.26" + peerDependencies: + "@nuxt/kit": ">=3.0.0" + react: ">=17.0.0" + vue: ">=3.0.0" + peerDependenciesMeta: + "@nuxt/kit": + optional: true + react: + optional: true + vue: + optional: true + checksum: 10c0/0d523ecd7bbbaac64b86de8e7c6641e4a2299065743def53eade177f42c280d1447d36b0fd7a525b7d69b0a889c1af91e85e48bfeec3851975c4858c0823204e + languageName: node + linkType: hard + +"libsql@npm:^0.4.4": + version: 0.4.7 + resolution: "libsql@npm:0.4.7" + dependencies: + "@libsql/darwin-arm64": "npm:0.4.7" + "@libsql/darwin-x64": "npm:0.4.7" + "@libsql/linux-arm64-gnu": "npm:0.4.7" + "@libsql/linux-arm64-musl": "npm:0.4.7" + "@libsql/linux-x64-gnu": "npm:0.4.7" + "@libsql/linux-x64-musl": "npm:0.4.7" + "@libsql/win32-x64-msvc": "npm:0.4.7" + "@neon-rs/load": "npm:^0.0.4" + detect-libc: "npm:2.0.2" + dependenciesMeta: + "@libsql/darwin-arm64": + optional: true + "@libsql/darwin-x64": + optional: true + "@libsql/linux-arm64-gnu": + optional: true + "@libsql/linux-arm64-musl": + optional: true + "@libsql/linux-x64-gnu": + optional: true + "@libsql/linux-x64-musl": + optional: true + "@libsql/win32-x64-msvc": + optional: true + checksum: 10c0/351952440e6bad3477e5f1bb1b9d6570d16e403b894f4a13c5c7e183a1307b2fb04a2fa902728cb8594a259e1726c51c61b822d545bbc88319b126ad15468a87 + conditions: (os=darwin | os=linux | os=win32) & (cpu=x64 | cpu=arm64 | cpu=wasm32) + languageName: node + linkType: hard + +"lightningcss-android-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-android-arm64@npm:1.32.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-android-arm64@npm:1.33.0": + version: 1.33.0 + resolution: "lightningcss-android-arm64@npm:1.33.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-arm64@npm:1.32.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-arm64@npm:1.33.0": + version: 1.33.0 + resolution: "lightningcss-darwin-arm64@npm:1.33.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-x64@npm:1.32.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-darwin-x64@npm:1.33.0": + version: 1.33.0 + resolution: "lightningcss-darwin-x64@npm:1.33.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-freebsd-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-freebsd-x64@npm:1.32.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-freebsd-x64@npm:1.33.0": + version: 1.33.0 + resolution: "lightningcss-freebsd-x64@npm:1.33.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-linux-arm-gnueabihf@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.32.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"lightningcss-linux-arm-gnueabihf@npm:1.33.0": + version: 1.33.0 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.33.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"lightningcss-linux-arm64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-gnu@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-arm64-gnu@npm:1.33.0": + version: 1.33.0 + resolution: "lightningcss-linux-arm64-gnu@npm:1.33.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-arm64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-musl@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-linux-arm64-musl@npm:1.33.0": + version: 1.33.0 + resolution: "lightningcss-linux-arm64-musl@npm:1.33.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-linux-x64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-gnu@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-x64-gnu@npm:1.33.0": + version: 1.33.0 + resolution: "lightningcss-linux-x64-gnu@npm:1.33.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-x64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-musl@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-linux-x64-musl@npm:1.33.0": + version: 1.33.0 + resolution: "lightningcss-linux-x64-musl@npm:1.33.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-win32-arm64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-arm64-msvc@npm:1.32.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-win32-arm64-msvc@npm:1.33.0": + version: 1.33.0 + resolution: "lightningcss-win32-arm64-msvc@npm:1.33.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-win32-x64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-x64-msvc@npm:1.32.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-win32-x64-msvc@npm:1.33.0": + version: 1.33.0 + resolution: "lightningcss-win32-x64-msvc@npm:1.33.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"lightningcss@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss@npm:1.32.0" + dependencies: + detect-libc: "npm:^2.0.3" + lightningcss-android-arm64: "npm:1.32.0" + lightningcss-darwin-arm64: "npm:1.32.0" + lightningcss-darwin-x64: "npm:1.32.0" + lightningcss-freebsd-x64: "npm:1.32.0" + lightningcss-linux-arm-gnueabihf: "npm:1.32.0" + lightningcss-linux-arm64-gnu: "npm:1.32.0" + lightningcss-linux-arm64-musl: "npm:1.32.0" + lightningcss-linux-x64-gnu: "npm:1.32.0" + lightningcss-linux-x64-musl: "npm:1.32.0" + lightningcss-win32-arm64-msvc: "npm:1.32.0" + lightningcss-win32-x64-msvc: "npm:1.32.0" + dependenciesMeta: + lightningcss-android-arm64: + optional: true + lightningcss-darwin-arm64: + optional: true + lightningcss-darwin-x64: + optional: true + lightningcss-freebsd-x64: + optional: true + lightningcss-linux-arm-gnueabihf: + optional: true + lightningcss-linux-arm64-gnu: + optional: true + lightningcss-linux-arm64-musl: + optional: true + lightningcss-linux-x64-gnu: + optional: true + lightningcss-linux-x64-musl: + optional: true + lightningcss-win32-arm64-msvc: + optional: true + lightningcss-win32-x64-msvc: + optional: true + checksum: 10c0/70945bd55097af46fc9fab7f5ed09cd5869d85940a2acab7ee06d0117004a1d68155708a2d462531cea2fc3c67aefc9333a7068c80b0b78dd404c16838809e03 + languageName: node + linkType: hard + +"lightningcss@npm:^1.33.0": + version: 1.33.0 + resolution: "lightningcss@npm:1.33.0" + dependencies: + detect-libc: "npm:^2.0.3" + lightningcss-android-arm64: "npm:1.33.0" + lightningcss-darwin-arm64: "npm:1.33.0" + lightningcss-darwin-x64: "npm:1.33.0" + lightningcss-freebsd-x64: "npm:1.33.0" + lightningcss-linux-arm-gnueabihf: "npm:1.33.0" + lightningcss-linux-arm64-gnu: "npm:1.33.0" + lightningcss-linux-arm64-musl: "npm:1.33.0" + lightningcss-linux-x64-gnu: "npm:1.33.0" + lightningcss-linux-x64-musl: "npm:1.33.0" + lightningcss-win32-arm64-msvc: "npm:1.33.0" + lightningcss-win32-x64-msvc: "npm:1.33.0" + dependenciesMeta: + lightningcss-android-arm64: + optional: true + lightningcss-darwin-arm64: + optional: true + lightningcss-darwin-x64: + optional: true + lightningcss-freebsd-x64: + optional: true + lightningcss-linux-arm-gnueabihf: + optional: true + lightningcss-linux-arm64-gnu: + optional: true + lightningcss-linux-arm64-musl: + optional: true + lightningcss-linux-x64-gnu: + optional: true + lightningcss-linux-x64-musl: + optional: true + lightningcss-win32-arm64-msvc: + optional: true + lightningcss-win32-x64-msvc: + optional: true + checksum: 10c0/ce1f8279fbae636dbf37fa6e7385d5f98ed881d72af3362f24afbd4685e19c1fcdfecf17e5dd77f2ebee3d0c23ade276230d85842d07292229a2cffba8ff20a3 + languageName: node + linkType: hard + +"lines-and-columns@npm:^1.1.6": + version: 1.2.4 + resolution: "lines-and-columns@npm:1.2.4" + checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d + languageName: node + linkType: hard + +"locate-path@npm:^7.2.0": + version: 7.2.0 + resolution: "locate-path@npm:7.2.0" + dependencies: + p-locate: "npm:^6.0.0" + checksum: 10c0/139e8a7fe11cfbd7f20db03923cacfa5db9e14fa14887ea121345597472b4a63c1a42a8a5187defeeff6acf98fd568da7382aa39682d38f0af27433953a97751 + languageName: node + linkType: hard + +"longest-streak@npm:^3.0.0": + version: 3.1.0 + resolution: "longest-streak@npm:3.1.0" + checksum: 10c0/7c2f02d0454b52834d1bcedef79c557bd295ee71fdabb02d041ff3aa9da48a90b5df7c0409156dedbc4df9b65da18742652aaea4759d6ece01f08971af6a7eaa + languageName: node + linkType: hard + +"loupe@npm:^3.1.0, loupe@npm:^3.1.4": + version: 3.2.1 + resolution: "loupe@npm:3.2.1" + checksum: 10c0/910c872cba291309664c2d094368d31a68907b6f5913e989d301b5c25f30e97d76d77f23ab3bf3b46d0f601ff0b6af8810c10c31b91d2c6b2f132809ca2cc705 + languageName: node + linkType: hard + +"lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb + languageName: node + linkType: hard + +"lru-cache@npm:^11.2.7": + version: 11.5.3 + resolution: "lru-cache@npm:11.5.3" + checksum: 10c0/2ad9bce6343a3544a1fd15364d8b35609aa52b2593d21fdc4877cabfe003b0f3deca4f9d6e9d13262f06e9e3d159de45c0a2f3edfc3adb46192333b5f146d8ed + languageName: node + linkType: hard + +"lru-cache@npm:^5.1.1": + version: 5.1.1 + resolution: "lru-cache@npm:5.1.1" + dependencies: + yallist: "npm:^3.0.2" + checksum: 10c0/89b2ef2ef45f543011e38737b8a8622a2f8998cddf0e5437174ef8f1f70a8b9d14a918ab3e232cb3ba343b7abddffa667f0b59075b2b80e6b4d63c3de6127482 + languageName: node + linkType: hard + +"magic-string@npm:^0.30.0, magic-string@npm:^0.30.17, magic-string@npm:^0.30.21": + version: 0.30.21 + resolution: "magic-string@npm:0.30.21" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.5" + checksum: 10c0/299378e38f9a270069fc62358522ddfb44e94244baa0d6a8980ab2a9b2490a1d03b236b447eee309e17eb3bddfa482c61259d47960eb018a904f0ded52780c4a + languageName: node + linkType: hard + +"magic-string@npm:^1.0.0": + version: 1.4.1 + resolution: "magic-string@npm:1.4.1" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.6.0" + checksum: 10c0/d5162ed2cef7ffa4ee5bcf44fbaba3a814f65b9919f0f3eba43076fda4223161b1064779da32901c3d1f157e7bbc0b6dd973e06c7856e550424f5e73e9217f0a + languageName: node + linkType: hard + +"magicast@npm:^0.5.2": + version: 0.5.5 + resolution: "magicast@npm:0.5.5" + dependencies: + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + source-map-js: "npm:^1.2.1" + checksum: 10c0/d27e94cd1e85f1736f97ab0c6e542170a818c7e573261722d056b3ec4a520e16d836ce4c6b0cd3dd215b1171d83592d48a3736f3da5997a55629a0de05f66e7d + languageName: node + linkType: hard + +"mdast-util-phrasing@npm:^4.0.0": + version: 4.1.0 + resolution: "mdast-util-phrasing@npm:4.1.0" + dependencies: + "@types/mdast": "npm:^4.0.0" + unist-util-is: "npm:^6.0.0" + checksum: 10c0/bf6c31d51349aa3d74603d5e5a312f59f3f65662ed16c58017169a5fb0f84ca98578f626c5ee9e4aa3e0a81c996db8717096705521bddb4a0185f98c12c9b42f + languageName: node + linkType: hard + +"mdast-util-to-hast@npm:^13.0.0": + version: 13.2.1 + resolution: "mdast-util-to-hast@npm:13.2.1" + dependencies: + "@types/hast": "npm:^3.0.0" + "@types/mdast": "npm:^4.0.0" + "@ungap/structured-clone": "npm:^1.0.0" + devlop: "npm:^1.0.0" + micromark-util-sanitize-uri: "npm:^2.0.0" + trim-lines: "npm:^3.0.0" + unist-util-position: "npm:^5.0.0" + unist-util-visit: "npm:^5.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/3eeaf28a5e84e1e08e6d54a1a8a06c0fca88cb5d36f4cf8086f0177248d1ce6e4e751f4ad0da19a3dea1c6ea61bd80784acc3ae021e44ceeb21aa5413a375e43 + languageName: node + linkType: hard + +"mdast-util-to-markdown@npm:^2.1.2": + version: 2.1.2 + resolution: "mdast-util-to-markdown@npm:2.1.2" + dependencies: + "@types/mdast": "npm:^4.0.0" + "@types/unist": "npm:^3.0.0" + longest-streak: "npm:^3.0.0" + mdast-util-phrasing: "npm:^4.0.0" + mdast-util-to-string: "npm:^4.0.0" + micromark-util-classify-character: "npm:^2.0.0" + micromark-util-decode-string: "npm:^2.0.0" + unist-util-visit: "npm:^5.0.0" + zwitch: "npm:^2.0.0" + checksum: 10c0/4649722a6099f12e797bd8d6469b2b43b44e526b5182862d9c7766a3431caad2c0112929c538a972f214e63c015395e5d3f54bd81d9ac1b16e6d8baaf582f749 + languageName: node + linkType: hard + +"mdast-util-to-string@npm:^4.0.0": + version: 4.0.0 + resolution: "mdast-util-to-string@npm:4.0.0" + dependencies: + "@types/mdast": "npm:^4.0.0" + checksum: 10c0/2d3c1af29bf3fe9c20f552ee9685af308002488f3b04b12fa66652c9718f66f41a32f8362aa2d770c3ff464c034860b41715902ada2306bb0a055146cef064d7 + languageName: node + linkType: hard + +"mdn-data@npm:2.0.28": + version: 2.0.28 + resolution: "mdn-data@npm:2.0.28" + checksum: 10c0/20000932bc4cd1cde9cba4e23f08cc4f816398af4c15ec81040ed25421d6bf07b5cf6b17095972577fb498988f40f4cb589e3169b9357bb436a12d8e07e5ea7b + languageName: node + linkType: hard + +"mdn-data@npm:2.27.1": + version: 2.27.1 + resolution: "mdn-data@npm:2.27.1" + checksum: 10c0/eb8abf5d22e4d1e090346f5e81b67d23cef14c83940e445da5c44541ad874dc8fb9f6ca236e8258c3a489d9fb5884188a4d7d58773adb9089ac2c0b966796393 + languageName: node + linkType: hard + +"micromark-util-character@npm:^2.0.0": + version: 2.1.1 + resolution: "micromark-util-character@npm:2.1.1" + dependencies: + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/d3fe7a5e2c4060fc2a076f9ce699c82a2e87190a3946e1e5eea77f563869b504961f5668d9c9c014724db28ac32fa909070ea8b30c3a39bd0483cc6c04cc76a1 + languageName: node + linkType: hard + +"micromark-util-classify-character@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-classify-character@npm:2.0.1" + dependencies: + micromark-util-character: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/8a02e59304005c475c332f581697e92e8c585bcd45d5d225a66c1c1b14ab5a8062705188c2ccec33cc998d33502514121478b2091feddbc751887fc9c290ed08 + languageName: node + linkType: hard + +"micromark-util-decode-numeric-character-reference@npm:^2.0.0": + version: 2.0.2 + resolution: "micromark-util-decode-numeric-character-reference@npm:2.0.2" + dependencies: + micromark-util-symbol: "npm:^2.0.0" + checksum: 10c0/9c8a9f2c790e5593ffe513901c3a110e9ec8882a08f466da014112a25e5059b51551ca0aeb7ff494657d86eceb2f02ee556c6558b8d66aadc61eae4a240da0df + languageName: node + linkType: hard + +"micromark-util-decode-string@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-decode-string@npm:2.0.1" + dependencies: + decode-named-character-reference: "npm:^1.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-decode-numeric-character-reference: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + checksum: 10c0/f24d75b2e5310be6e7b6dee532e0d17d3bf46996841d6295f2a9c87a2046fff4ab603c52ab9d7a7a6430a8b787b1574ae895849c603d262d1b22eef71736b5cb + languageName: node + linkType: hard + +"micromark-util-encode@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-encode@npm:2.0.1" + checksum: 10c0/b2b29f901093845da8a1bf997ea8b7f5e061ffdba85070dfe14b0197c48fda64ffcf82bfe53c90cf9dc185e69eef8c5d41cae3ba918b96bc279326921b59008a + languageName: node + linkType: hard + +"micromark-util-sanitize-uri@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-sanitize-uri@npm:2.0.1" + dependencies: + micromark-util-character: "npm:^2.0.0" + micromark-util-encode: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + checksum: 10c0/60e92166e1870fd4f1961468c2651013ff760617342918e0e0c3c4e872433aa2e60c1e5a672bfe5d89dc98f742d6b33897585cf86ae002cda23e905a3c02527c + languageName: node + linkType: hard + +"micromark-util-symbol@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-symbol@npm:2.0.1" + checksum: 10c0/f2d1b207771e573232436618e78c5e46cd4b5c560dd4a6d63863d58018abbf49cb96ec69f7007471e51434c60de3c9268ef2bf46852f26ff4aacd10f9da16fe9 + languageName: node + linkType: hard + +"micromark-util-types@npm:^2.0.0": + version: 2.0.2 + resolution: "micromark-util-types@npm:2.0.2" + checksum: 10c0/c8c15b96c858db781c4393f55feec10004bf7df95487636c9a9f7209e51002a5cca6a047c5d2a5dc669ff92da20e57aaa881e81a268d9ccadb647f9dce305298 + languageName: node + linkType: hard + +"mime-db@npm:^1.54.0": + version: 1.54.0 + resolution: "mime-db@npm:1.54.0" + checksum: 10c0/8d907917bc2a90fa2df842cdf5dfeaf509adc15fe0531e07bb2f6ab15992416479015828d6a74200041c492e42cce3ebf78e5ce714388a0a538ea9c53eece284 + languageName: node + linkType: hard + +"mime-types@npm:^3.0.2": + version: 3.0.2 + resolution: "mime-types@npm:3.0.2" + dependencies: + mime-db: "npm:^1.54.0" + checksum: 10c0/35a0dd1035d14d185664f346efcdb72e93ef7a9b6e9ae808bd1f6358227010267fab52657b37562c80fc888ff76becb2b2938deb5e730818b7983bf8bd359767 + languageName: node + linkType: hard + +"min-indent@npm:^1.0.0": + version: 1.0.1 + resolution: "min-indent@npm:1.0.1" + checksum: 10c0/7e207bd5c20401b292de291f02913230cb1163abca162044f7db1d951fa245b174dc00869d40dd9a9f32a885ad6a5f3e767ee104cf278f399cb4e92d3f582d5c + languageName: node + linkType: hard + +"minimatch@npm:^9.0.4": + version: 9.0.9 + resolution: "minimatch@npm:9.0.9" + dependencies: + brace-expansion: "npm:^2.0.2" + checksum: 10c0/0b6a58530dbb00361745aa6c8cffaba4c90f551afe7c734830bd95fd88ebf469dd7355a027824ea1d09e37181cfeb0a797fb17df60c15ac174303ac110eb7e86 + languageName: node + linkType: hard + +"minimist@npm:^1.2.6": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.4, minipass@npm:^7.1.2": + version: 7.1.3 + resolution: "minipass@npm:7.1.3" + checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb + languageName: node + linkType: hard + +"minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" + dependencies: + minipass: "npm:^7.1.2" + checksum: 10c0/5aad75ab0090b8266069c9aabe582c021ae53eb33c6c691054a13a45db3b4f91a7fb1bd79151e6b4e9e9a86727b522527c0a06ec7d45206b745d54cd3097bcec + languageName: node + linkType: hard + +"mrmime@npm:^2.0.1": + version: 2.0.1 + resolution: "mrmime@npm:2.0.1" + checksum: 10c0/af05afd95af202fdd620422f976ad67dc18e6ee29beb03dd1ce950ea6ef664de378e44197246df4c7cdd73d47f2e7143a6e26e473084b9e4aa2095c0ad1e1761 + languageName: node + linkType: hard + +"ms@npm:^2.1.3": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + +"muggle-string@npm:^0.4.1": + version: 0.4.1 + resolution: "muggle-string@npm:0.4.1" + checksum: 10c0/e914b63e24cd23f97e18376ec47e4ba3aa24365e4776212b666add2e47bb158003212980d732c49abf3719568900af7861873844a6e2d3a7ca7e86952c0e99e9 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.18": + version: 3.3.19 + resolution: "nanoid@npm:3.3.19" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/ce7afa6258bfaa00bdc8de291cfe9913280eaa403b7d9cff3db4e4200c1913e7163577df74897958da144db473a96c67f74f57db0f542fbfe409df677bae3255 + languageName: node + linkType: hard + +"neotraverse@npm:^1.0.1": + version: 1.0.1 + resolution: "neotraverse@npm:1.0.1" + checksum: 10c0/98e5d2d1974f91d527851a9dcfceb62701a1d31970b324b9f35fbdd244d173edc7a379ea1773f64771a9dc282486e3d3b693b24e14b1e969291612b2ff6b5054 + languageName: node + linkType: hard + +"nlcst-to-string@npm:^4.0.0": + version: 4.0.0 + resolution: "nlcst-to-string@npm:4.0.0" + dependencies: + "@types/nlcst": "npm:^2.0.0" + checksum: 10c0/a192c8b3365a7c076812004e72ae5b4a1734e582be2a6f3c062f3beecf18868a9fe2d1bad870bfead320fb39830f2c4f3752e5ae6574c4e59157126fd1ddba70 + languageName: node + linkType: hard + +"node-domexception@npm:^1.0.0": + version: 1.0.0 + resolution: "node-domexception@npm:1.0.0" + checksum: 10c0/5e5d63cda29856402df9472335af4bb13875e1927ad3be861dc5ebde38917aecbf9ae337923777af52a48c426b70148815e890a5d72760f1b4d758cc671b1a2b + languageName: node + linkType: hard + +"node-fetch-native@npm:^1.6.7": + version: 1.6.7 + resolution: "node-fetch-native@npm:1.6.7" + checksum: 10c0/8b748300fb053d21ca4d3db9c3ff52593d5e8f8a2d9fe90cbfad159676e324b954fdaefab46aeca007b5b9edab3d150021c4846444e4e8ab1f4e44cd3807be87 + languageName: node + linkType: hard + +"node-fetch@npm:^3.3.2": + version: 3.3.2 + resolution: "node-fetch@npm:3.3.2" + dependencies: + data-uri-to-buffer: "npm:^4.0.0" + fetch-blob: "npm:^3.1.4" + formdata-polyfill: "npm:^4.0.10" + checksum: 10c0/f3d5e56190562221398c9f5750198b34cf6113aa304e34ee97c94fd300ec578b25b2c2906edba922050fce983338fde0d5d34fcb0fc3336ade5bd0e429ad7538 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 13.0.2 + resolution: "node-gyp@npm:13.0.2" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + graceful-fs: "npm:^4.2.6" + nopt: "npm:^10.0.0" + proc-log: "npm:^7.0.0" + semver: "npm:^7.3.5" + tar: "npm:^7.5.4" + tinyglobby: "npm:^0.2.12" + undici: "npm:^8.4.1" + which: "npm:^7.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10c0/29d33ccf47ffeeb5e7af925550b416f698a26a72eb10975c7eb5775c1d0cecacd76d164a4aa45503d3ddcece209db65c712be00423c69381a4cd14e2e6540559 + languageName: node + linkType: hard + +"node-mock-http@npm:^1.0.4": + version: 1.0.5 + resolution: "node-mock-http@npm:1.0.5" + checksum: 10c0/14a36b28423953af95e4659aa1e07208b5a38daaa89c1aac28a193f5420744a2511eb288503019b2fc8de582c70612d2e475d0df1d1a0e44fe3dfd7fc8f5399c + languageName: node + linkType: hard + +"node-releases@npm:^2.0.55": + version: 2.0.56 + resolution: "node-releases@npm:2.0.56" + checksum: 10c0/d62240b1440f660a6742e0a45ed7eef14bc79228447042ddb58363f73f4a5971b40fc5647f2e40f26854b24ac575ad4a21453d627c3554b4d4b31b4d596d6e8e + languageName: node + linkType: hard + +"nopt@npm:^10.0.0": + version: 10.0.1 + resolution: "nopt@npm:10.0.1" + dependencies: + abbrev: "npm:^5.0.0" + bin: + nopt: bin/nopt.js + checksum: 10c0/980d89257f9587f3e1f77877ddbf905d6aa3b738ec33e49a4fa1a059a0dd82eb28063982b150654a7ae9de386f2ead60e56172db7d37cf56de545f7392a2a26a + languageName: node + linkType: hard + +"normalize-path@npm:^3.0.0": + version: 3.0.0 + resolution: "normalize-path@npm:3.0.0" + checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 + languageName: node + linkType: hard + +"nth-check@npm:^2.1.1": + version: 2.1.1 + resolution: "nth-check@npm:2.1.1" + dependencies: + boolbase: "npm:^1.0.0" + checksum: 10c0/5fee7ff309727763689cfad844d979aedd2204a817fbaaf0e1603794a7c20db28548d7b024692f953557df6ce4a0ee4ae46cd8ebd9b36cfb300b9226b567c479 + languageName: node + linkType: hard + +"obug@npm:^2.1.1": + version: 2.2.1 + resolution: "obug@npm:2.2.1" + checksum: 10c0/8a7c001847d880fa6ba83c7bbee6a80fd271ee628439cd508c8647d53b217abb1759e2d5e4aaeb09a35d0a274bdb225a0a5a78eded3213d63e656153cf411b63 + languageName: node + linkType: hard + +"ofetch@npm:^1.5.1": + version: 1.5.1 + resolution: "ofetch@npm:1.5.1" + dependencies: + destr: "npm:^2.0.5" + node-fetch-native: "npm:^1.6.7" + ufo: "npm:^1.6.1" + checksum: 10c0/97ebc600512ea0ab401e97c73313218cc53c9b530b32ec8c995c347b0c68887129993168d1753f527761a64c6f93a5d823ce1378ccec95fc65a606f323a79a6c + languageName: node + linkType: hard + +"ohash@npm:^2.0.11": + version: 2.0.12 + resolution: "ohash@npm:2.0.12" + checksum: 10c0/29a47fa554bfc80d5fcea6ee347296de97cc900abd7d3f502ac3379ce7631770a66c6e942582e0f1ddc8fb2b1246521383d8d6f634fd4f9a0935e315af728b61 + languageName: node + linkType: hard + +"on-finished@npm:^2.4.1": + version: 2.4.1 + resolution: "on-finished@npm:2.4.1" + dependencies: + ee-first: "npm:1.1.1" + checksum: 10c0/46fb11b9063782f2d9968863d9cbba33d77aa13c17f895f56129c274318b86500b22af3a160fe9995aa41317efcd22941b6eba747f718ced08d9a73afdb087b4 + languageName: node + linkType: hard + +"oniguruma-parser@npm:^0.12.2": + version: 0.12.2 + resolution: "oniguruma-parser@npm:0.12.2" + checksum: 10c0/fe5255d2cd5a6b845d5a0abe1725898ef40cea5522290dba6ccc08cc388891e9e8007af4baa5059942b786ad44b2b677ef25948039c9ba3f057bd35d2c52076a + languageName: node + linkType: hard + +"oniguruma-to-es@npm:^4.3.6": + version: 4.3.6 + resolution: "oniguruma-to-es@npm:4.3.6" + dependencies: + oniguruma-parser: "npm:^0.12.2" + regex: "npm:^6.1.0" + regex-recursion: "npm:^6.0.2" + checksum: 10c0/044e08b98e706987c2882ccf228de2a671de4aff33e3bd370da137ba599c0d520549625ad26ecc8898502445c991e19f106f584e94382262ba2af02c908ca3cf + languageName: node + linkType: hard + +"open@npm:^8.0.4": + version: 8.4.2 + resolution: "open@npm:8.4.2" + dependencies: + define-lazy-prop: "npm:^2.0.0" + is-docker: "npm:^2.1.1" + is-wsl: "npm:^2.2.0" + checksum: 10c0/bb6b3a58401dacdb0aad14360626faf3fb7fba4b77816b373495988b724fb48941cad80c1b65d62bb31a17609b2cd91c41a181602caea597ca80dfbcc27e84c9 + languageName: node + linkType: hard + +"p-limit@npm:^4.0.0": + version: 4.0.0 + resolution: "p-limit@npm:4.0.0" + dependencies: + yocto-queue: "npm:^1.0.0" + checksum: 10c0/a56af34a77f8df2ff61ddfb29431044557fcbcb7642d5a3233143ebba805fc7306ac1d448de724352861cb99de934bc9ab74f0d16fe6a5460bdbdf938de875ad + languageName: node + linkType: hard + +"p-limit@npm:^7.3.0": + version: 7.3.3 + resolution: "p-limit@npm:7.3.3" + dependencies: + yocto-queue: "npm:^1.2.1" + checksum: 10c0/47e0125583d81c2f5a32c96dd4a1c63cc3fc106ae8b94d00ec152caabd1f64fe22cfa58ba8b7eef47a89991fa64b3403d1549e1e41470dc787d6074f34015f34 + languageName: node + linkType: hard + +"p-locate@npm:^6.0.0": + version: 6.0.0 + resolution: "p-locate@npm:6.0.0" + dependencies: + p-limit: "npm:^4.0.0" + checksum: 10c0/d72fa2f41adce59c198270aa4d3c832536c87a1806e0f69dffb7c1a7ca998fb053915ca833d90f166a8c082d3859eabfed95f01698a3214c20df6bb8de046312 + languageName: node + linkType: hard + +"p-queue@npm:^9.1.0": + version: 9.3.3 + resolution: "p-queue@npm:9.3.3" + dependencies: + eventemitter3: "npm:^5.0.4" + p-timeout: "npm:^7.0.0" + checksum: 10c0/0d88a35cb3301bee2aac654c0a7018a2e40775fa597f3a06da66f33b0311d106bebbe9732c8e5968ae1e0e0b49f79c874333d2a7b0a57fad469189f236c066d7 + languageName: node + linkType: hard + +"p-timeout@npm:^7.0.0": + version: 7.0.2 + resolution: "p-timeout@npm:7.0.2" + checksum: 10c0/26595bbda27361a15231a82d8d3ef4f277bb3e9647aaa4a554b90012d03d22aaf28165b60afffe9425fb1326bd40b93f145d76eb23adb6b76d07d5f5d4c7964a + languageName: node + linkType: hard + +"package-json-from-dist@npm:^1.0.0": + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b + languageName: node + linkType: hard + +"package-manager-detector@npm:^1.6.0": + version: 1.8.0 + resolution: "package-manager-detector@npm:1.8.0" + checksum: 10c0/4c8e2c47fdc874465d3b3b11934401db9d73470ccbdabeb092e46cb94abbc491d5befc8c271b5ea1ae9c6a881f44c2997e011873365bde9fd6c3dc001221ff7a + languageName: node + linkType: hard + +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: "npm:^3.0.0" + checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556 + languageName: node + linkType: hard + +"parse-json@npm:^5.2.0": + version: 5.2.0 + resolution: "parse-json@npm:5.2.0" + dependencies: + "@babel/code-frame": "npm:^7.0.0" + error-ex: "npm:^1.3.1" + json-parse-even-better-errors: "npm:^2.3.0" + lines-and-columns: "npm:^1.1.6" + checksum: 10c0/77947f2253005be7a12d858aedbafa09c9ae39eb4863adf330f7b416ca4f4a08132e453e08de2db46459256fb66afaac5ee758b44fe6541b7cdaf9d252e59585 + languageName: node + linkType: hard + +"path-browserify@npm:^1.0.1": + version: 1.0.1 + resolution: "path-browserify@npm:1.0.1" + checksum: 10c0/8b8c3fd5c66bd340272180590ae4ff139769e9ab79522e2eb82e3d571a89b8117c04147f65ad066dccfb42fcad902e5b7d794b3d35e0fd840491a8ddbedf8c66 + languageName: node + linkType: hard + +"path-exists@npm:^5.0.0": + version: 5.0.0 + resolution: "path-exists@npm:5.0.0" + checksum: 10c0/b170f3060b31604cde93eefdb7392b89d832dfbc1bed717c9718cbe0f230c1669b7e75f87e19901da2250b84d092989a0f9e44d2ef41deb09aa3ad28e691a40a + languageName: node + linkType: hard + +"path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c + languageName: node + linkType: hard + +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 10c0/11ce261f9d294cc7a58d6a574b7f1b935842355ec66fba3c3fd79e0f036462eaf07d0aa95bb74ff432f9afef97ce1926c720988c6a7451d8a584930ae7de86e1 + languageName: node + linkType: hard + +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d + languageName: node + linkType: hard + +"pathval@npm:^2.0.0": + version: 2.0.1 + resolution: "pathval@npm:2.0.1" + checksum: 10c0/460f4709479fbf2c45903a65655fc8f0a5f6d808f989173aeef5fdea4ff4f303dc13f7870303999add60ec49d4c14733895c0a869392e9866f1091fa64fd7581 + languageName: node + linkType: hard + +"piccolore@npm:^0.1.3": + version: 0.1.3 + resolution: "piccolore@npm:0.1.3" + checksum: 10c0/999666bb32eccc96a26b0cf3b8afe72f9d4cd4ca0eab5802b404cc84c601d81db4485b5feee5c50dbc8436ba6e1bc6a2310f99c18cee6687e430cc0eb4a36470 + languageName: node + linkType: hard + +"picocolors@npm:^1.1.1": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 + languageName: node + linkType: hard + +"picomatch@npm:^2.0.4": + version: 2.3.2 + resolution: "picomatch@npm:2.3.2" + checksum: 10c0/a554d1709e59be97d1acb9eaedbbc700a5c03dbd4579807baed95100b00420bc729335440ef15004ae2378984e2487a7c1cebd743cfdb72b6fa9ab69223c0d61 + languageName: node + linkType: hard + +"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3, picomatch@npm:^4.0.4, picomatch@npm:^4.0.7": + version: 4.0.7 + resolution: "picomatch@npm:4.0.7" + checksum: 10c0/beb6ae02c43ae44e84883b90830196d9046b1726ead292adcf7f57945e0bb0d992d68563d87e03b484b6f3c9a5c6defda7523477f047d7f0e663f126cc01787f + languageName: node + linkType: hard + +"postcss@npm:^8.5.28, postcss@npm:^8.5.3, postcss@npm:^8.5.6": + version: 8.5.28 + resolution: "postcss@npm:8.5.28" + dependencies: + nanoid: "npm:^3.3.18" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/9fe44215a6628d89c8a75184b92f5b2f77e16f9e5b13b39b9009bb7e8e6eccddf82c8854bae922db1f17ace6ef3445073fe38abff513caeb03e5285b1b55620a + languageName: node + linkType: hard + +"prettier@npm:^3.8.1": + version: 3.9.8 + resolution: "prettier@npm:3.9.8" + bin: + prettier: bin/prettier.cjs + checksum: 10c0/257e5ef11ba5006d0b0712271705701c065c8003e07763f996ce185d39633902f3fe1f814839eed76f75c97b6d5376d6157bd9f44db18cacb1dbdcb263cc66ae + languageName: node + linkType: hard + +"prismjs@npm:^1.30.0": + version: 1.30.0 + resolution: "prismjs@npm:1.30.0" + checksum: 10c0/f56205bfd58ef71ccfcbcb691fd0eb84adc96c6ff21b0b69fc6fdcf02be42d6ef972ba4aed60466310de3d67733f6a746f89f2fb79c00bf217406d465b3e8f23 + languageName: node + linkType: hard + +"proc-log@npm:^7.0.0": + version: 7.0.0 + resolution: "proc-log@npm:7.0.0" + checksum: 10c0/b89c2d862604f35fec795477b0c7e376feab3ba0d4f4d291c4e959567442697cf451ac557d0623c1cc38af45a78128b983410f397a10c5d3a67f76c33de4754b + languageName: node + linkType: hard + +"process-ancestry@npm:^0.1.0": + version: 0.1.0 + resolution: "process-ancestry@npm:0.1.0" + checksum: 10c0/3b20263db7c2eeb567efcef21bf4b3f3363c4bf7041796943fd8a467f888d94eb093a022b8fcc49c8d843f3fc81d15047255d4c2b4990f2d5a4f851f3c5c9cd4 + languageName: node + linkType: hard + +"promise-limit@npm:^2.7.0": + version: 2.7.0 + resolution: "promise-limit@npm:2.7.0" + checksum: 10c0/ce220a7e11c8d0541940a3d99cc424bd16a18451b295a263f6dbaa998585d2d1afa71fcb7bb29078a61e214d2f13d96e9b082e96e8e357fbe5c5936ef2459cba + languageName: node + linkType: hard + +"property-information@npm:^7.0.0": + version: 7.2.0 + resolution: "property-information@npm:7.2.0" + checksum: 10c0/03662c8f9e1544510914c5e594ae72963f67d261027a5fdc06c4134742584fe40dd49b959470d30e757e9fb70b297d8546ab1362a040364144029926ad4e07c4 + languageName: node + linkType: hard + +"radix3@npm:^1.1.2": + version: 1.1.2 + resolution: "radix3@npm:1.1.2" + checksum: 10c0/d4a295547f71af079868d2c2ed3814a9296ee026c5488212d58c106e6b4797c6eaec1259b46c9728913622f2240c9a944bfc8e2b3b5f6e4a5045338b1609f1e4 + languageName: node + linkType: hard + +"range-parser@npm:^1.2.1": + version: 1.3.0 + resolution: "range-parser@npm:1.3.0" + checksum: 10c0/295494bb6685f9ab50f41a5f4fa5ce445aeeb9673b491bf178460fcc3a812dcf0d164cf1c83074f13a71a87d91ead5e097afd53e0630bc49a5101ed60f2a7529 + languageName: node + linkType: hard + +"react-docgen-typescript@npm:^2.2.2": + version: 2.4.0 + resolution: "react-docgen-typescript@npm:2.4.0" + peerDependencies: + typescript: ">= 4.3.x" + checksum: 10c0/18e3e1c80d28abcdd72e62261d2f70b0904d9b088f9c2ebe485ffee5e46f5735208bc174a20ed2772112b3ca6432b5f3d5f0ac345872fe76e541f84543e49e50 + languageName: node + linkType: hard + +"react-docgen@npm:^8.0.0": + version: 8.0.4 + resolution: "react-docgen@npm:8.0.4" + dependencies: + "@babel/core": "npm:^7.28.0" + "@babel/traverse": "npm:^7.28.0" + "@babel/types": "npm:^7.28.2" + "@types/babel__core": "npm:^7.20.5" + "@types/babel__traverse": "npm:^7.20.7" + "@types/doctrine": "npm:^0.0.9" + "@types/resolve": "npm:^1.20.2" + doctrine: "npm:^3.0.0" + resolve: "npm:^1.22.1" + strip-indent: "npm:^4.0.0" + checksum: 10c0/08a97df288791b2bf4ae2edadae0818726a9cc3b4d25f5e68b3f182629bb51ba61a66c48c04146625426d040287b596f3cba411358814355260db437940b2766 + languageName: node + linkType: hard + +"react-dom@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0, react-dom@npm:^19.1.0": + version: 19.3.0 + resolution: "react-dom@npm:19.3.0" + dependencies: + scheduler: "npm:^0.28.0" + peerDependencies: + react: ^19.3.0 + checksum: 10c0/c513044b724dd6bfe9500a9682d60973e7f8a48cffba00b6b16d3e910e27dcc459bf71bf0a5c33d7d62a3843ca30de1bd1e2098058aa4ed71f618a24b6061da5 + languageName: node + linkType: hard + +"react-refresh@npm:^0.17.0": + version: 0.17.0 + resolution: "react-refresh@npm:0.17.0" + checksum: 10c0/002cba940384c9930008c0bce26cac97a9d5682bc623112c2268ba0c155127d9c178a9a5cc2212d560088d60dfd503edd808669a25f9b377f316a32361d0b23c + languageName: node + linkType: hard + +"react-remove-scroll-bar@npm:^2.3.7": + version: 2.3.8 + resolution: "react-remove-scroll-bar@npm:2.3.8" + dependencies: + react-style-singleton: "npm:^2.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/9a0675c66cbb52c325bdbfaed80987a829c4504cefd8ff2dd3b6b3afc9a1500b8ec57b212e92c1fb654396d07bbe18830a8146fe77677d2a29ce40b5e1f78654 + languageName: node + linkType: hard + +"react-remove-scroll@npm:^2.7.2": + version: 2.7.2 + resolution: "react-remove-scroll@npm:2.7.2" + dependencies: + react-remove-scroll-bar: "npm:^2.3.7" + react-style-singleton: "npm:^2.2.3" + tslib: "npm:^2.1.0" + use-callback-ref: "npm:^1.3.3" + use-sidecar: "npm:^1.1.3" + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/b5f3315bead75e72853f492c0b51ba8fb4fa09a4534d7fc42d6fcd59ca3e047cf213279ffc1e35b337e314ef5a04cb2b12544c85e0078802271731c27c09e5aa + languageName: node + linkType: hard + +"react-style-singleton@npm:^2.2.2, react-style-singleton@npm:^2.2.3": + version: 2.2.3 + resolution: "react-style-singleton@npm:2.2.3" + dependencies: + get-nonce: "npm:^1.0.0" + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/841938ff16d16a6b76895f4cb2e1fea957e5fe3b30febbf03a54892dae1c9153f2383e231dea0b3ba41192ad2f2849448fa859caccd288943bce32639e971bee + languageName: node + linkType: hard + +"react@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0, react@npm:^19.1.0": + version: 19.3.0 + resolution: "react@npm:19.3.0" + checksum: 10c0/dd3099c3c43a7122bc01223bb26b0f8d5ba13cca569af6fb94a005fb54fa73223c9cb203bb226d40cfd49a82cbeef2e36ce84062a3a6d5b9fe8f4437694c7b52 + languageName: node + linkType: hard + +"readdirp@npm:^4.0.1": + version: 4.1.2 + resolution: "readdirp@npm:4.1.2" + checksum: 10c0/60a14f7619dec48c9c850255cd523e2717001b0e179dc7037cfa0895da7b9e9ab07532d324bfb118d73a710887d1e35f79c495fa91582784493e085d18c72c62 + languageName: node + linkType: hard + +"readdirp@npm:^5.0.0": + version: 5.1.1 + resolution: "readdirp@npm:5.1.1" + checksum: 10c0/5e99755684b9104761e48801828f4d2614efa33b1698629929c5985184dc63a7f8233c827832c40dc7be16b00cc871d303cf9de396d2fb74b9465ee2aa2424a1 + languageName: node + linkType: hard + +"recast@npm:^0.23.5": + version: 0.23.21 + resolution: "recast@npm:0.23.21" + dependencies: + ast-types: "npm:^0.16.1" + esprima: "npm:~4.0.0" + source-map: "npm:~0.6.1" + tiny-invariant: "npm:^1.3.3" + tslib: "npm:^2.0.1" + checksum: 10c0/afee4673fda5a95b0db8199a3f75c6f675269f00328a3673f241a6088c99b5a58f7082b6cff683456a182e3302bfb57313ebb1c2073228b260724b0993ff1f9a + languageName: node + linkType: hard + +"redent@npm:^3.0.0": + version: 3.0.0 + resolution: "redent@npm:3.0.0" + dependencies: + indent-string: "npm:^4.0.0" + strip-indent: "npm:^3.0.0" + checksum: 10c0/d64a6b5c0b50eb3ddce3ab770f866658a2b9998c678f797919ceb1b586bab9259b311407280bd80b804e2a7c7539b19238ae6a2a20c843f1a7fcff21d48c2eae + languageName: node + linkType: hard + +"regex-recursion@npm:^6.0.2": + version: 6.0.2 + resolution: "regex-recursion@npm:6.0.2" + dependencies: + regex-utilities: "npm:^2.3.0" + checksum: 10c0/68e8b6889680e904b75d7f26edaf70a1a4dc1087406bff53face4c2929d918fd77c72223843fe816ac8ed9964f96b4160650e8d5909e26a998c6e9de324dadb1 + languageName: node + linkType: hard + +"regex-utilities@npm:^2.3.0": + version: 2.3.0 + resolution: "regex-utilities@npm:2.3.0" + checksum: 10c0/78c550a80a0af75223244fff006743922591bd8f61d91fef7c86b9b56cf9bbf8ee5d7adb6d8991b5e304c57c90103fc4818cf1e357b11c6c669b782839bd7893 + languageName: node + linkType: hard + +"regex@npm:^6.1.0": + version: 6.1.0 + resolution: "regex@npm:6.1.0" + dependencies: + regex-utilities: "npm:^2.3.0" + checksum: 10c0/6e0ee2a1c17d5a66dc1120dfc51899dedf6677857e83a0df4d5a822ebb8645a54a079772efc1ade382b67aad35e4e22b5bd2d33c05ed28b0e000f8f57eb0aec1 + languageName: node + linkType: hard + +"request-light@npm:^0.5.7": + version: 0.5.8 + resolution: "request-light@npm:0.5.8" + checksum: 10c0/65d47f79cbe13c46c10eecb9264ad06ea5f76ea64855766448937c0fe888a70b9c854bf9dc4c2dc1949ea5400030b55bea7d4ba0807fae0202004cee796ea819 + languageName: node + linkType: hard + +"request-light@npm:^0.7.0": + version: 0.7.0 + resolution: "request-light@npm:0.7.0" + checksum: 10c0/1c98f0d74b8a28a6fecb96d4b30c255a27b402f5e10b2696b67ede7257389a7533fba9e8ea122ccec9a7c6f06db36eab5d0b563bd5ff27b15aaa9d3910e31894 + languageName: node + linkType: hard + +"require-from-string@npm:^2.0.2": + version: 2.0.2 + resolution: "require-from-string@npm:2.0.2" + checksum: 10c0/aaa267e0c5b022fc5fd4eef49d8285086b15f2a1c54b28240fdf03599cbd9c26049fee3eab894f2e1f6ca65e513b030a7c264201e3f005601e80c49fb2937ce2 + languageName: node + linkType: hard + +"resolve-from@npm:^4.0.0": + version: 4.0.0 + resolution: "resolve-from@npm:4.0.0" + checksum: 10c0/8408eec31a3112ef96e3746c37be7d64020cda07c03a920f5024e77290a218ea758b26ca9529fd7b1ad283947f34b2291c1c0f6aa0ed34acfdda9c6014c8d190 + languageName: node + linkType: hard + +"resolve-from@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 + languageName: node + linkType: hard + +"resolve-pkg-maps@npm:^1.0.0": + version: 1.0.0 + resolution: "resolve-pkg-maps@npm:1.0.0" + checksum: 10c0/fb8f7bbe2ca281a73b7ef423a1cbc786fb244bd7a95cbe5c3fba25b27d327150beca8ba02f622baea65919a57e061eb5005204daa5f93ed590d9b77463a567ab + languageName: node + linkType: hard + +"resolve@npm:^1.22.1, resolve@npm:^1.22.8": + version: 1.22.12 + resolution: "resolve@npm:1.22.12" + dependencies: + es-errors: "npm:^1.3.0" + is-core-module: "npm:^2.16.1" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/b16dc9b537c02e8c3388f7d3dcff9741d3071625f9a97ac1c885f2b0ca51e78df22328fb6d6ef214dd9101fb7cfc19aa2836fe3410402a94f3f7b8639c7149bf + languageName: node + linkType: hard + +"resolve@patch:resolve@npm%3A^1.22.1#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin<compat/resolve>": + version: 1.22.12 + resolution: "resolve@patch:resolve@npm%3A1.22.12#optional!builtin<compat/resolve>::version=1.22.12&hash=9bd1a5" + dependencies: + es-errors: "npm:^1.3.0" + is-core-module: "npm:^2.16.1" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/e12af106e16e652bd83060e8afacece4ee3dcbd0f4cf028e3d9ad178e6ced9ecb1c87ee60158b239582313902a717f8adc57a9b76ea54b1994141ba15f420c65 + languageName: node + linkType: hard + +"retext-smartypants@npm:^6.2.0": + version: 6.2.0 + resolution: "retext-smartypants@npm:6.2.0" + dependencies: + "@types/nlcst": "npm:^2.0.0" + nlcst-to-string: "npm:^4.0.0" + unist-util-visit: "npm:^5.0.0" + checksum: 10c0/36f925353dd7f31df642bca2493524a8daee15f9b0e0dfe7fb8982462d23ccb12a99864989db22f0bacb6d7fea1f696ba96e031d3fbac4f013e1c95ef3fed881 + languageName: node + linkType: hard + +"rolldown@npm:~1.2.6": + version: 1.2.9 + resolution: "rolldown@npm:1.2.9" + dependencies: + "@oxc-project/types": "npm:=0.150.0" + "@rolldown/binding-android-arm-eabi": "npm:1.2.9" + "@rolldown/binding-android-arm64": "npm:1.2.9" + "@rolldown/binding-darwin-arm64": "npm:1.2.9" + "@rolldown/binding-darwin-x64": "npm:1.2.9" + "@rolldown/binding-freebsd-x64": "npm:1.2.9" + "@rolldown/binding-linux-arm-gnueabihf": "npm:1.2.9" + "@rolldown/binding-linux-arm64-gnu": "npm:1.2.9" + "@rolldown/binding-linux-arm64-musl": "npm:1.2.9" + "@rolldown/binding-linux-ppc64-gnu": "npm:1.2.9" + "@rolldown/binding-linux-s390x-gnu": "npm:1.2.9" + "@rolldown/binding-linux-x64-gnu": "npm:1.2.9" + "@rolldown/binding-linux-x64-musl": "npm:1.2.9" + "@rolldown/binding-openharmony-arm64": "npm:1.2.9" + "@rolldown/binding-win32-arm64-msvc": "npm:1.2.9" + "@rolldown/binding-win32-x64-msvc": "npm:1.2.9" + "@rolldown/pluginutils": "npm:^1.0.0" + dependenciesMeta: + "@rolldown/binding-android-arm-eabi": + optional: true + "@rolldown/binding-android-arm64": + optional: true + "@rolldown/binding-darwin-arm64": + optional: true + "@rolldown/binding-darwin-x64": + optional: true + "@rolldown/binding-freebsd-x64": + optional: true + "@rolldown/binding-linux-arm-gnueabihf": + optional: true + "@rolldown/binding-linux-arm64-gnu": + optional: true + "@rolldown/binding-linux-arm64-musl": + optional: true + "@rolldown/binding-linux-ppc64-gnu": + optional: true + "@rolldown/binding-linux-s390x-gnu": + optional: true + "@rolldown/binding-linux-x64-gnu": + optional: true + "@rolldown/binding-linux-x64-musl": + optional: true + "@rolldown/binding-openharmony-arm64": + optional: true + "@rolldown/binding-win32-arm64-msvc": + optional: true + "@rolldown/binding-win32-x64-msvc": + optional: true + bin: + rolldown: ./bin/cli.mjs + checksum: 10c0/701019ab29fab27e5465b11ec482eb3f7e87766f9ecbbd618d3d26821437eb84fa9e852d1cdef41c4e2e525904e6a9d3974ede9fbbdc887c9da9a3981bf8946c + languageName: node + linkType: hard + +"rollup@npm:^4.34.9, rollup@npm:^4.43.0": + version: 4.63.4 + resolution: "rollup@npm:4.63.4" + dependencies: + "@napi-rs/lzma-linux-x64-gnu": "npm:1.5.1" + "@rollup/rollup-android-arm-eabi": "npm:4.63.4" + "@rollup/rollup-android-arm64": "npm:4.63.4" + "@rollup/rollup-darwin-arm64": "npm:4.63.4" + "@rollup/rollup-darwin-x64": "npm:4.63.4" + "@rollup/rollup-freebsd-arm64": "npm:4.63.4" + "@rollup/rollup-freebsd-x64": "npm:4.63.4" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.63.4" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.63.4" + "@rollup/rollup-linux-arm64-gnu": "npm:4.63.4" + "@rollup/rollup-linux-arm64-musl": "npm:4.63.4" + "@rollup/rollup-linux-loong64-gnu": "npm:4.63.4" + "@rollup/rollup-linux-loong64-musl": "npm:4.63.4" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.63.4" + "@rollup/rollup-linux-ppc64-musl": "npm:4.63.4" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.63.4" + "@rollup/rollup-linux-riscv64-musl": "npm:4.63.4" + "@rollup/rollup-linux-s390x-gnu": "npm:4.63.4" + "@rollup/rollup-linux-x64-gnu": "npm:4.63.4" + "@rollup/rollup-linux-x64-musl": "npm:4.63.4" + "@rollup/rollup-openbsd-x64": "npm:4.63.4" + "@rollup/rollup-openharmony-arm64": "npm:4.63.4" + "@rollup/rollup-win32-arm64-msvc": "npm:4.63.4" + "@rollup/rollup-win32-ia32-msvc": "npm:4.63.4" + "@rollup/rollup-win32-x64-gnu": "npm:4.63.4" + "@rollup/rollup-win32-x64-msvc": "npm:4.63.4" + "@types/estree": "npm:1.0.9" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@napi-rs/lzma-linux-x64-gnu": + optional: true + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-freebsd-arm64": + optional: true + "@rollup/rollup-freebsd-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-loong64-gnu": + optional: true + "@rollup/rollup-linux-loong64-musl": + optional: true + "@rollup/rollup-linux-ppc64-gnu": + optional: true + "@rollup/rollup-linux-ppc64-musl": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-openbsd-x64": + optional: true + "@rollup/rollup-openharmony-arm64": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-gnu": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/fb0aa9242126b8d01196a394edc98423d36e802d13741f9ec6f58559cda783b5e6d46a160f5cf8be6492c13df46786253c0a45267f0dac0294f6ca7d926ea916 + languageName: node + linkType: hard + +"satteri@npm:^0.10.3": + version: 0.10.5 + resolution: "satteri@npm:0.10.5" + dependencies: + "@bruits/satteri-darwin-arm64": "npm:0.10.5" + "@bruits/satteri-darwin-x64": "npm:0.10.5" + "@bruits/satteri-linux-arm64-gnu": "npm:0.10.5" + "@bruits/satteri-linux-arm64-musl": "npm:0.10.5" + "@bruits/satteri-linux-x64-gnu": "npm:0.10.5" + "@bruits/satteri-linux-x64-musl": "npm:0.10.5" + "@bruits/satteri-wasm32-wasi": "npm:0.10.5" + "@bruits/satteri-win32-arm64-msvc": "npm:0.10.5" + "@bruits/satteri-win32-x64-msvc": "npm:0.10.5" + "@types/estree-jsx": "npm:^1.0.5" + "@types/hast": "npm:^3.0.5" + "@types/mdast": "npm:^4.0.4" + "@types/unist": "npm:^3.0.3" + dependenciesMeta: + "@bruits/satteri-darwin-arm64": + optional: true + "@bruits/satteri-darwin-x64": + optional: true + "@bruits/satteri-linux-arm64-gnu": + optional: true + "@bruits/satteri-linux-arm64-musl": + optional: true + "@bruits/satteri-linux-x64-gnu": + optional: true + "@bruits/satteri-linux-x64-musl": + optional: true + "@bruits/satteri-wasm32-wasi": + optional: true + "@bruits/satteri-win32-arm64-msvc": + optional: true + "@bruits/satteri-win32-x64-msvc": + optional: true + checksum: 10c0/e1a9f6f9784c8ea79be59ddb803403999521632ef57a595579e1e310646172c9661a0ce41c2667ed9b82a55d697dd192f6470e9a6ee348d5b207172d89d0cba3 + languageName: node + linkType: hard + +"sax@npm:1.6.1, sax@npm:^1.4.1": + version: 1.6.1 + resolution: "sax@npm:1.6.1" + checksum: 10c0/c91a71043d60da50fdf1e2cee936fc7ad4c9376afb8d1022aef5655f279433640859ec06b3b49abfcbe578fc7da2828cc1661e45aaedf835f5393496193437fa + languageName: node + linkType: hard + +"scheduler@npm:^0.28.0": + version: 0.28.0 + resolution: "scheduler@npm:0.28.0" + checksum: 10c0/783c8e5ce89d41273a02ed6dc0e3d24bd60e05c9e8f7e3adfc6c82e7c69935500d90e343f1ee26d592fc7504e8a2532573c6368def3e93bfe812391fa53efaf4 + languageName: node + linkType: hard + +"schema-dts-lib@npm:^1.0.0": + version: 1.0.0 + resolution: "schema-dts-lib@npm:1.0.0" + peerDependencies: + typescript: ">=4.9.5" + checksum: 10c0/18b5d1ceb57f1e57073fe5a94c035201b924562dfed77ebf472767fde853014d7d0baab31e2006383c07b5608439b6e72c375cd9798c4d655a6c514355fdb527 + languageName: node + linkType: hard + +"schema-dts@npm:^2.0.0": + version: 2.0.0 + resolution: "schema-dts@npm:2.0.0" + dependencies: + schema-dts-lib: "npm:^1.0.0" + checksum: 10c0/139e2178b0609549338e9b8b970e34f44f342a3f75911806d0e4221f1e0ab2c09a482133f9fa1f55a29c4ec7fdb3cace056853368a9db487c572bc408db2b2c9 + languageName: node + linkType: hard + +"semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d + languageName: node + linkType: hard + +"semver@npm:^7.3.5, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.6.0, semver@npm:^7.6.2, semver@npm:^7.8.5": + version: 7.8.5 + resolution: "semver@npm:7.8.5" + bin: + semver: bin/semver.js + checksum: 10c0/b1f3127a5be8125a94f37188b361c212466c292c6910adce3ec106cff5dc211ccaedc4739c11bb70fda59d6fc1f040a9bca289f4e093451521a2372e5231fe0c + languageName: node + linkType: hard + +"send@npm:^1.2.1": + version: 1.2.1 + resolution: "send@npm:1.2.1" + dependencies: + debug: "npm:^4.4.3" + encodeurl: "npm:^2.0.0" + escape-html: "npm:^1.0.3" + etag: "npm:^1.8.1" + fresh: "npm:^2.0.0" + http-errors: "npm:^2.0.1" + mime-types: "npm:^3.0.2" + ms: "npm:^2.1.3" + on-finished: "npm:^2.4.1" + range-parser: "npm:^1.2.1" + statuses: "npm:^2.0.2" + checksum: 10c0/fbbbbdc902a913d65605274be23f3d604065cfc3ee3d78bf9fc8af1dc9fc82667c50d3d657f5e601ac657bac9b396b50ee97bd29cd55436320cf1cddebdcec72 + languageName: node + linkType: hard + +"serialize-javascript@npm:^7.1.1": + version: 7.1.1 + resolution: "serialize-javascript@npm:7.1.1" + checksum: 10c0/a9209f0c3e946cc4421b705cfa502a28a7a5fed65eee22cb66b4200f374b2c3b8c9d0fadb6a00c75d7c0cd09c2780d1e0f390c75dce9d6dfb171f0b63c85014a + languageName: node + linkType: hard + +"server-destroy@npm:^1.0.1": + version: 1.0.1 + resolution: "server-destroy@npm:1.0.1" + checksum: 10c0/ab771f12b24cde1bc622cce0c14a1365f3ab0685937c641c762916fb43aa7a03d6c577cc4375ea5361d6bc1bb20ab689ec5723086e5798c6781edb61287e24b8 + languageName: node + linkType: hard + +"setprototypeof@npm:~1.2.0": + version: 1.2.0 + resolution: "setprototypeof@npm:1.2.0" + checksum: 10c0/68733173026766fa0d9ecaeb07f0483f4c2dc70ca376b3b7c40b7cda909f94b0918f6c5ad5ce27a9160bdfb475efaa9d5e705a11d8eaae18f9835d20976028bc + languageName: node + linkType: hard + +"sharp@npm:^0.35.4": + version: 0.35.4 + resolution: "sharp@npm:0.35.4" + dependencies: + "@img/colour": "npm:^1.1.0" + "@img/sharp-darwin-arm64": "npm:0.35.4" + "@img/sharp-darwin-x64": "npm:0.35.4" + "@img/sharp-freebsd-wasm32": "npm:0.35.4" + "@img/sharp-libvips-darwin-arm64": "npm:1.3.3" + "@img/sharp-libvips-darwin-x64": "npm:1.3.3" + "@img/sharp-libvips-linux-arm": "npm:1.3.3" + "@img/sharp-libvips-linux-arm64": "npm:1.3.3" + "@img/sharp-libvips-linux-ppc64": "npm:1.3.3" + "@img/sharp-libvips-linux-riscv64": "npm:1.3.3" + "@img/sharp-libvips-linux-s390x": "npm:1.3.3" + "@img/sharp-libvips-linux-x64": "npm:1.3.3" + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.3.3" + "@img/sharp-libvips-linuxmusl-x64": "npm:1.3.3" + "@img/sharp-linux-arm": "npm:0.35.4" + "@img/sharp-linux-arm64": "npm:0.35.4" + "@img/sharp-linux-ppc64": "npm:0.35.4" + "@img/sharp-linux-riscv64": "npm:0.35.4" + "@img/sharp-linux-s390x": "npm:0.35.4" + "@img/sharp-linux-x64": "npm:0.35.4" + "@img/sharp-linuxmusl-arm64": "npm:0.35.4" + "@img/sharp-linuxmusl-x64": "npm:0.35.4" + "@img/sharp-webcontainers-wasm32": "npm:0.35.4" + "@img/sharp-win32-arm64": "npm:0.35.4" + "@img/sharp-win32-ia32": "npm:0.35.4" + "@img/sharp-win32-x64": "npm:0.35.4" + detect-libc: "npm:^2.1.2" + semver: "npm:^7.8.5" + dependenciesMeta: + "@img/sharp-darwin-arm64": + optional: true + "@img/sharp-darwin-x64": + optional: true + "@img/sharp-freebsd-wasm32": + optional: true + "@img/sharp-libvips-darwin-arm64": + optional: true + "@img/sharp-libvips-darwin-x64": + optional: true + "@img/sharp-libvips-linux-arm": + optional: true + "@img/sharp-libvips-linux-arm64": + optional: true + "@img/sharp-libvips-linux-ppc64": + optional: true + "@img/sharp-libvips-linux-riscv64": + optional: true + "@img/sharp-libvips-linux-s390x": + optional: true + "@img/sharp-libvips-linux-x64": + optional: true + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + "@img/sharp-libvips-linuxmusl-x64": + optional: true + "@img/sharp-linux-arm": + optional: true + "@img/sharp-linux-arm64": + optional: true + "@img/sharp-linux-ppc64": + optional: true + "@img/sharp-linux-riscv64": + optional: true + "@img/sharp-linux-s390x": + optional: true + "@img/sharp-linux-x64": + optional: true + "@img/sharp-linuxmusl-arm64": + optional: true + "@img/sharp-linuxmusl-x64": + optional: true + "@img/sharp-webcontainers-wasm32": + optional: true + "@img/sharp-win32-arm64": + optional: true + "@img/sharp-win32-ia32": + optional: true + "@img/sharp-win32-x64": + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/dbcc0909200cbff98e82805c86085b94afbd45f373897a04b21e52e9bbc1671dc64970350dd3cb6b2d0970f3444be7100d728005dc6620e39b599269ed7e15eb + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: "npm:^3.0.0" + checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690 + languageName: node + linkType: hard + +"shell-quote@npm:^1.8.1": + version: 1.10.0 + resolution: "shell-quote@npm:1.10.0" + checksum: 10c0/46ee59bfd972ce6a45500c44ed130dff2d0a7d6fbac9841e59d548518cad8060a06393c9a5dcbc0cede294ad80b2a2cd8c904679e09265f53efc0a0879f30961 + languageName: node + linkType: hard + +"shiki@npm:^4.0.2": + version: 4.4.3 + resolution: "shiki@npm:4.4.3" + dependencies: + "@shikijs/core": "npm:4.4.3" + "@shikijs/engine-javascript": "npm:4.4.3" + "@shikijs/engine-oniguruma": "npm:4.4.3" + "@shikijs/langs": "npm:4.4.3" + "@shikijs/themes": "npm:4.4.3" + "@shikijs/types": "npm:4.4.3" + "@shikijs/vscode-textmate": "npm:^10.0.2" + "@types/hast": "npm:^3.0.5" + checksum: 10c0/deff32ff8069de44c8b0f11296088eafa9cece8e3aa0817b4a52637a31f291d4da08f1133f19bea1a976dda026386862cd131f37bfe6562c1b15932d25ee460e + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 + languageName: node + linkType: hard + +"sisteransi@npm:^1.0.5": + version: 1.0.5 + resolution: "sisteransi@npm:1.0.5" + checksum: 10c0/230ac975cca485b7f6fe2b96a711aa62a6a26ead3e6fb8ba17c5a00d61b8bed0d7adc21f5626b70d7c33c62ff4e63933017a6462942c719d1980bb0b1207ad46 + languageName: node + linkType: hard + +"sitemap@npm:^9.0.1": + version: 9.0.1 + resolution: "sitemap@npm:9.0.1" + dependencies: + "@types/node": "npm:^24.9.2" + "@types/sax": "npm:^1.2.1" + arg: "npm:^5.0.0" + sax: "npm:^1.4.1" + bin: + sitemap: dist/esm/cli.js + checksum: 10c0/dddd3c9f30fd09655fa2c1bd9bb182ed860717b3ba681fdc1ac66ad25b5f399dafdd8b87d5ba4ee914ea14e7b11841aadc3af61e4a45242a14f28194efe12416 + languageName: node + linkType: hard + +"smol-toml@npm:^1.6.0": + version: 1.8.0 + resolution: "smol-toml@npm:1.8.0" + checksum: 10c0/4e01dbfb7c7af142176b037ebedaccccafb8229cc873feaa47c184ee31073d86e30b4b1b0fe9a5a6d083ed3ce21615eda9e81d1f5d779ad8b2e2e9459b900aea + languageName: node + linkType: hard + +"sonner@npm:^2.0.8": + version: 2.0.8 + resolution: "sonner@npm:2.0.8" + peerDependencies: + "@types/react": ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/cc271f78dca7e7d7ce34a724883f3292ba671b5ed3d41732430df28c38a8f6a47e8e59da5fdd0b258ac07136db0cdaf02f8cda9afb9cc9f12d81b6c319b427f7 + languageName: node + linkType: hard + +"source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf + languageName: node + linkType: hard + +"source-map-support@npm:^0.5.21": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 10c0/9ee09942f415e0f721d6daad3917ec1516af746a8120bba7bb56278707a37f1eb8642bde456e98454b8a885023af81a16e646869975f06afc1a711fb90484e7d + languageName: node + linkType: hard + +"source-map@npm:^0.6.0, source-map@npm:~0.6.1": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + +"space-separated-tokens@npm:^2.0.0": + version: 2.0.2 + resolution: "space-separated-tokens@npm:2.0.2" + checksum: 10c0/6173e1d903dca41dcab6a2deed8b4caf61bd13b6d7af8374713500570aa929ff9414ae09a0519f4f8772df993300305a395d4871f35bc4ca72b6db57e1f30af8 + languageName: node + linkType: hard + +"statuses@npm:^2.0.2, statuses@npm:~2.0.2": + version: 2.0.2 + resolution: "statuses@npm:2.0.2" + checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f + languageName: node + linkType: hard + +"storybook@npm:^9.1.10": + version: 9.1.20 + resolution: "storybook@npm:9.1.20" + dependencies: + "@storybook/global": "npm:^5.0.0" + "@testing-library/jest-dom": "npm:^6.6.3" + "@testing-library/user-event": "npm:^14.6.1" + "@vitest/expect": "npm:3.2.4" + "@vitest/mocker": "npm:3.2.4" + "@vitest/spy": "npm:3.2.4" + better-opn: "npm:^3.0.2" + esbuild: "npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0" + esbuild-register: "npm:^3.5.0" + recast: "npm:^0.23.5" + semver: "npm:^7.6.2" + ws: "npm:^8.18.0" + peerDependencies: + prettier: ^2 || ^3 + peerDependenciesMeta: + prettier: + optional: true + bin: + storybook: ./bin/index.cjs + checksum: 10c0/d510837e81297e83119a8cf827818231ff9c99299d7c4f29407a8317ac151803d8784d891fa1a4983be1998417abbcaad27cd21e47dd33f4da2d246014eb68cf + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b + languageName: node + linkType: hard + +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca + languageName: node + linkType: hard + +"string-width@npm:^7.0.0, string-width@npm:^7.2.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 + languageName: node + linkType: hard + +"string-width@npm:^8.2.1": + version: 8.2.2 + resolution: "string-width@npm:8.2.2" + dependencies: + get-east-asian-width: "npm:^1.5.0" + strip-ansi: "npm:^7.1.2" + checksum: 10c0/895624534e4e2f229e880bbc30aa77bdeb758e1a0edce203e0a17b8b4f08b8d3c1156516efc50a35e3efd0052d938a73797692111250ac9f52ee384710a01714 + languageName: node + linkType: hard + +"stringify-entities@npm:^4.0.0": + version: 4.0.4 + resolution: "stringify-entities@npm:4.0.4" + dependencies: + character-entities-html4: "npm:^2.0.0" + character-entities-legacy: "npm:^3.0.0" + checksum: 10c0/537c7e656354192406bdd08157d759cd615724e9d0873602d2c9b2f6a5c0a8d0b1d73a0a08677848105c5eebac6db037b57c0b3a4ec86331117fa7319ed50448 + languageName: node + linkType: hard + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 + languageName: node + linkType: hard + +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0, strip-ansi@npm:^7.1.2": + version: 7.2.0 + resolution: "strip-ansi@npm:7.2.0" + dependencies: + ansi-regex: "npm:^6.2.2" + checksum: 10c0/544d13b7582f8254811ea97db202f519e189e59d35740c46095897e254e4f1aa9fe1524a83ad6bc5ad67d4dd6c0281d2e0219ed62b880a6238a16a17d375f221 + languageName: node + linkType: hard + +"strip-bom@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-bom@npm:3.0.0" + checksum: 10c0/51201f50e021ef16672593d7434ca239441b7b760e905d9f33df6e4f3954ff54ec0e0a06f100d028af0982d6f25c35cd5cda2ce34eaebccd0250b8befb90d8f1 + languageName: node + linkType: hard + +"strip-indent@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-indent@npm:3.0.0" + dependencies: + min-indent: "npm:^1.0.0" + checksum: 10c0/ae0deaf41c8d1001c5d4fbe16cb553865c1863da4fae036683b474fa926af9fc121e155cb3fc57a68262b2ae7d5b8420aa752c97a6428c315d00efe2a3875679 + languageName: node + linkType: hard + +"strip-indent@npm:^4.0.0": + version: 4.1.1 + resolution: "strip-indent@npm:4.1.1" + checksum: 10c0/5b23dd5934be0ef6b6fe1b802887f83e56ad9dcd9f6c3896a637da2c6c3a6da3fdf3e51354a98e6cccb6f1c41863e7b9b9deaa348639dfd35f71f3549edb4dff + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 10c0/6c4032340701a9950865f7ae8ef38578d8d7053f5e10518076e6554a9381fa91bd9c6850193695c141f32b21f979c985db07265a758867bac95de05f7d8aeb39 + languageName: node + linkType: hard + +"svgo@npm:^4.0.2": + version: 4.1.0 + resolution: "svgo@npm:4.1.0" + dependencies: + commander: "npm:^11.1.0" + css-select: "npm:^6.0.0" + css-tree: "npm:^3.0.1" + css-what: "npm:^7.0.0" + csso: "npm:^5.0.5" + picocolors: "npm:^1.1.1" + sax: "npm:1.6.1" + bin: + svgo: bin/svgo.js + checksum: 10c0/2c6dc6975d98ead8fd1982f9abd6d9bc9a0b9d1a00ef9781c1c9f900af2f64f4d5d7c9af4051504bdf0e111eadc3be9b31984c5c49320df74704d995e37fac4b + languageName: node + linkType: hard + +"tailwind-merge@npm:^3.7.0": + version: 3.7.0 + resolution: "tailwind-merge@npm:3.7.0" + checksum: 10c0/1b1439fe30b6ee63fc3abc1c985bf9beb8529eb2db21cc321bd8c7cf73f36cd4ec80846ea10af8ae09175810f385336c22a6ce355a0b5cce69432d49672f90c1 + languageName: node + linkType: hard + +"tailwindcss@npm:4.3.3, tailwindcss@npm:^4.3.3": + version: 4.3.3 + resolution: "tailwindcss@npm:4.3.3" + checksum: 10c0/7e9cd7553cd890ffa5350efb0ca8971ba5435257d0f98bf0714994ac8f1fee5c7cdf9a9da47fe83e8ac995d728410837ac49d3ef5ea66b5afe1db312086ffae2 + languageName: node + linkType: hard + +"tapable@npm:^2.3.3": + version: 2.3.3 + resolution: "tapable@npm:2.3.3" + checksum: 10c0/47992e861053f861154e92fb4a98ac4ab47b6463717e60792dd1e8c755da0c4964cd8bb68c308a9066d6da89000b6310457b4d5d985c30de4ccc29066068cc17 + languageName: node + linkType: hard + +"tar@npm:^7.5.4": + version: 7.5.22 + resolution: "tar@npm:7.5.22" + dependencies: + "@isaacs/fs-minipass": "npm:^4.0.0" + chownr: "npm:^3.0.0" + minipass: "npm:^7.1.2" + minizlib: "npm:^3.1.0" + yallist: "npm:^5.0.0" + checksum: 10c0/1311f6be85a8157ac4c9147bae43e13923d2a1aae15e4aa1bd5239e4e03d2cf53cfe103dde7f35832fbb4c938b042856bc8e9a0afd29abd05e2d1608788c4fea + languageName: node + linkType: hard + +"tiny-inflate@npm:^1.0.3": + version: 1.0.3 + resolution: "tiny-inflate@npm:1.0.3" + checksum: 10c0/fab687537254f6ec44c9a2e880048fe70da3542aba28f73cda3e74c95cabf342a339372f2a6c032e322324f01accc03ca26c04ba2bad9b3eb8cf3ee99bba7f9b + languageName: node + linkType: hard + +"tiny-invariant@npm:^1.3.3": + version: 1.3.3 + resolution: "tiny-invariant@npm:1.3.3" + checksum: 10c0/65af4a07324b591a059b35269cd696aba21bef2107f29b9f5894d83cc143159a204b299553435b03874ebb5b94d019afa8b8eff241c8a4cfee95872c2e1c1c4a + languageName: node + linkType: hard + +"tinyclip@npm:^0.1.12": + version: 0.1.15 + resolution: "tinyclip@npm:0.1.15" + checksum: 10c0/8ebdcc60e0e9ba54edeae28158a7a96e94657ac9387cc03cd5ef6c51c36b92be2ebe10cf5e34c9382124ef766cf82784cca2c5db57cd7ab447b7d04880ee7579 + languageName: node + linkType: hard + +"tinyexec@npm:^1.0.0, tinyexec@npm:^1.0.4": + version: 1.3.1 + resolution: "tinyexec@npm:1.3.1" + checksum: 10c0/c590369cb3fa73cc18a3998caec8a00f9712d91309c881be3ac52b160fb2f80c7b1fbf3591858ab6abab47ad55385b0c86ec19982e32441a01f55298dab9ccd2 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13, tinyglobby@npm:^0.2.15, tinyglobby@npm:^0.2.16, tinyglobby@npm:^0.2.17": + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c + languageName: node + linkType: hard + +"tinyrainbow@npm:^2.0.0": + version: 2.0.0 + resolution: "tinyrainbow@npm:2.0.0" + checksum: 10c0/c83c52bef4e0ae7fb8ec6a722f70b5b6fa8d8be1c85792e829f56c0e1be94ab70b293c032dc5048d4d37cfe678f1f5babb04bdc65fd123098800148ca989184f + languageName: node + linkType: hard + +"tinyspy@npm:^4.0.3": + version: 4.0.6 + resolution: "tinyspy@npm:4.0.6" + checksum: 10c0/1066760cea2a18ca69b9d934b73212ff5a45414387a67270b6944591a8afe59031e3d82b48cf7ca2d16b6c640d654e3a4a5726a63f2509aec3ef2d98f62ae5b8 + languageName: node + linkType: hard + +"toidentifier@npm:~1.0.1": + version: 1.0.1 + resolution: "toidentifier@npm:1.0.1" + checksum: 10c0/93937279934bd66cc3270016dd8d0afec14fb7c94a05c72dc57321f8bd1fa97e5bea6d1f7c89e728d077ca31ea125b78320a616a6c6cd0e6b9cb94cb864381c1 + languageName: node + linkType: hard + +"trim-lines@npm:^3.0.0": + version: 3.0.1 + resolution: "trim-lines@npm:3.0.1" + checksum: 10c0/3a1611fa9e52aa56a94c69951a9ea15b8aaad760eaa26c56a65330dc8adf99cb282fc07cc9d94968b7d4d88003beba220a7278bbe2063328eb23fb56f9509e94 + languageName: node + linkType: hard + +"trough@npm:^2.0.0": + version: 2.2.0 + resolution: "trough@npm:2.2.0" + checksum: 10c0/58b671fc970e7867a48514168894396dd94e6d9d6456aca427cc299c004fe67f35ed7172a36449086b2edde10e78a71a284ec0076809add6834fb8f857ccb9b0 + languageName: node + linkType: hard + +"ts-dedent@npm:^2.0.0": + version: 2.3.0 + resolution: "ts-dedent@npm:2.3.0" + checksum: 10c0/bfc3331e0740191c0134fb526e7f01e16657e50955c9395de14703c6ef17119c91651fa044cf558dc9c1dbb0fe1b2a74e303aeebcbd5e3b31acd14f72f545a54 + languageName: node + linkType: hard + +"tsconfig-paths@npm:^4.2.0": + version: 4.2.0 + resolution: "tsconfig-paths@npm:4.2.0" + dependencies: + json5: "npm:^2.2.2" + minimist: "npm:^1.2.6" + strip-bom: "npm:^3.0.0" + checksum: 10c0/09a5877402d082bb1134930c10249edeebc0211f36150c35e1c542e5b91f1047b1ccf7da1e59babca1ef1f014c525510f4f870de7c9bda470c73bb4e2721b3ea + languageName: node + linkType: hard + +"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.8.1": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + +"tsx@npm:^4.19.4": + version: 4.23.15 + resolution: "tsx@npm:4.23.15" + dependencies: + esbuild: "npm:~0.28.0" + fsevents: "npm:~2.3.3" + dependenciesMeta: + fsevents: + optional: true + bin: + tsx: dist/cli.mjs + checksum: 10c0/3c5fc53e578da87d3a63565286b1ecbb0e01b9bb646daec1957294ef890d14f22cc67bb4b09b5daffab0e7ce0217b1e7645393feb0f524b1ddf26ae9595d7e75 + languageName: node + linkType: hard + +"turbo@npm:^2.11.2": + version: 2.11.2 + resolution: "turbo@npm:2.11.2" + dependencies: + "@turbo/darwin-64": "npm:2.11.2" + "@turbo/darwin-arm64": "npm:2.11.2" + "@turbo/linux-64": "npm:2.11.2" + "@turbo/linux-arm64": "npm:2.11.2" + "@turbo/windows-64": "npm:2.11.2" + "@turbo/windows-arm64": "npm:2.11.2" + dependenciesMeta: + "@turbo/darwin-64": + optional: true + "@turbo/darwin-arm64": + optional: true + "@turbo/linux-64": + optional: true + "@turbo/linux-arm64": + optional: true + "@turbo/windows-64": + optional: true + "@turbo/windows-arm64": + optional: true + bin: + turbo: bin/turbo + checksum: 10c0/51efad5766483dc8cef9ebfddea452f68d23d09bcef5488b4f4adcefc655ce878959c0daa9e214382587a38147c8ee25a621973e3e780400e496ea3e80f8c60a + languageName: node + linkType: hard + +"typesafe-path@npm:^0.2.2": + version: 0.2.2 + resolution: "typesafe-path@npm:0.2.2" + checksum: 10c0/05027ea6a52a1e879da39c53fae7f5059e50cf578d586cc6c7741c3eca0b37ee2da4ef16d35ecf9f60cd0e8ca5aca98ea47902672907a19470f2260e8769b933 + languageName: node + linkType: hard + +"typescript-auto-import-cache@npm:^0.3.5": + version: 0.3.6 + resolution: "typescript-auto-import-cache@npm:0.3.6" + dependencies: + semver: "npm:^7.3.8" + checksum: 10c0/aad48d6aa9985f9a056c5c89ab91b6781c5430c0b80652586c2a997f2cc394f3ed70ec18c0c818a8423991a1cfe7e64d010cc2fded9ea192067a3188476c2b51 + languageName: node + linkType: hard + +"typescript@npm:^7.0.2": + version: 7.0.2 + resolution: "typescript@npm:7.0.2" + dependencies: + "@typescript/typescript-aix-ppc64": "npm:7.0.2" + "@typescript/typescript-darwin-arm64": "npm:7.0.2" + "@typescript/typescript-darwin-x64": "npm:7.0.2" + "@typescript/typescript-freebsd-arm64": "npm:7.0.2" + "@typescript/typescript-freebsd-x64": "npm:7.0.2" + "@typescript/typescript-linux-arm": "npm:7.0.2" + "@typescript/typescript-linux-arm64": "npm:7.0.2" + "@typescript/typescript-linux-loong64": "npm:7.0.2" + "@typescript/typescript-linux-mips64el": "npm:7.0.2" + "@typescript/typescript-linux-ppc64": "npm:7.0.2" + "@typescript/typescript-linux-riscv64": "npm:7.0.2" + "@typescript/typescript-linux-s390x": "npm:7.0.2" + "@typescript/typescript-linux-x64": "npm:7.0.2" + "@typescript/typescript-netbsd-arm64": "npm:7.0.2" + "@typescript/typescript-netbsd-x64": "npm:7.0.2" + "@typescript/typescript-openbsd-arm64": "npm:7.0.2" + "@typescript/typescript-openbsd-x64": "npm:7.0.2" + "@typescript/typescript-sunos-x64": "npm:7.0.2" + "@typescript/typescript-win32-arm64": "npm:7.0.2" + "@typescript/typescript-win32-x64": "npm:7.0.2" + dependenciesMeta: + "@typescript/typescript-aix-ppc64": + optional: true + "@typescript/typescript-darwin-arm64": + optional: true + "@typescript/typescript-darwin-x64": + optional: true + "@typescript/typescript-freebsd-arm64": + optional: true + "@typescript/typescript-freebsd-x64": + optional: true + "@typescript/typescript-linux-arm": + optional: true + "@typescript/typescript-linux-arm64": + optional: true + "@typescript/typescript-linux-loong64": + optional: true + "@typescript/typescript-linux-mips64el": + optional: true + "@typescript/typescript-linux-ppc64": + optional: true + "@typescript/typescript-linux-riscv64": + optional: true + "@typescript/typescript-linux-s390x": + optional: true + "@typescript/typescript-linux-x64": + optional: true + "@typescript/typescript-netbsd-arm64": + optional: true + "@typescript/typescript-netbsd-x64": + optional: true + "@typescript/typescript-openbsd-arm64": + optional: true + "@typescript/typescript-openbsd-x64": + optional: true + "@typescript/typescript-sunos-x64": + optional: true + "@typescript/typescript-win32-arm64": + optional: true + "@typescript/typescript-win32-x64": + optional: true + bin: + tsc: bin/tsc + checksum: 10c0/3dcee51ec8c332492325bcdbf7df48b66668751f127e622ae7d41b6c716eb19184424a45e14f7750f50f340232388b69e6287859155f06a5ce2df76f12cb31cf + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A^7.0.2#optional!builtin<compat/typescript>": + version: 7.0.2 + resolution: "typescript@patch:typescript@npm%3A7.0.2#optional!builtin<compat/typescript>::version=7.0.2&hash=3bafbf" + dependencies: + "@typescript/typescript-aix-ppc64": "npm:7.0.2" + "@typescript/typescript-darwin-arm64": "npm:7.0.2" + "@typescript/typescript-darwin-x64": "npm:7.0.2" + "@typescript/typescript-freebsd-arm64": "npm:7.0.2" + "@typescript/typescript-freebsd-x64": "npm:7.0.2" + "@typescript/typescript-linux-arm": "npm:7.0.2" + "@typescript/typescript-linux-arm64": "npm:7.0.2" + "@typescript/typescript-linux-loong64": "npm:7.0.2" + "@typescript/typescript-linux-mips64el": "npm:7.0.2" + "@typescript/typescript-linux-ppc64": "npm:7.0.2" + "@typescript/typescript-linux-riscv64": "npm:7.0.2" + "@typescript/typescript-linux-s390x": "npm:7.0.2" + "@typescript/typescript-linux-x64": "npm:7.0.2" + "@typescript/typescript-netbsd-arm64": "npm:7.0.2" + "@typescript/typescript-netbsd-x64": "npm:7.0.2" + "@typescript/typescript-openbsd-arm64": "npm:7.0.2" + "@typescript/typescript-openbsd-x64": "npm:7.0.2" + "@typescript/typescript-sunos-x64": "npm:7.0.2" + "@typescript/typescript-win32-arm64": "npm:7.0.2" + "@typescript/typescript-win32-x64": "npm:7.0.2" + dependenciesMeta: + "@typescript/typescript-aix-ppc64": + optional: true + "@typescript/typescript-darwin-arm64": + optional: true + "@typescript/typescript-darwin-x64": + optional: true + "@typescript/typescript-freebsd-arm64": + optional: true + "@typescript/typescript-freebsd-x64": + optional: true + "@typescript/typescript-linux-arm": + optional: true + "@typescript/typescript-linux-arm64": + optional: true + "@typescript/typescript-linux-loong64": + optional: true + "@typescript/typescript-linux-mips64el": + optional: true + "@typescript/typescript-linux-ppc64": + optional: true + "@typescript/typescript-linux-riscv64": + optional: true + "@typescript/typescript-linux-s390x": + optional: true + "@typescript/typescript-linux-x64": + optional: true + "@typescript/typescript-netbsd-arm64": + optional: true + "@typescript/typescript-netbsd-x64": + optional: true + "@typescript/typescript-openbsd-arm64": + optional: true + "@typescript/typescript-openbsd-x64": + optional: true + "@typescript/typescript-sunos-x64": + optional: true + "@typescript/typescript-win32-arm64": + optional: true + "@typescript/typescript-win32-x64": + optional: true + bin: + tsc: bin/tsc + checksum: 10c0/6b3cdc369cd829d46e00734ea64233ee84419c2825ad8fe408915adde77abced4b7a2401f7eed517d54a7a4f5d1c1869753cb99018df1c2159c761d9f33483a6 + languageName: node + linkType: hard + +"ufo@npm:^1.6.1, ufo@npm:^1.6.3": + version: 1.6.4 + resolution: "ufo@npm:1.6.4" + checksum: 10c0/3a2b29e7e3d772fbf6893d7d23bf442981457adb2fe122828abdbda89bedcb81aafd0dcc080e41b45f9a877db00cb42cbfee9639753a19d9b9bd39b5627039cf + languageName: node + linkType: hard + +"ultrahtml@npm:^1.6.0": + version: 1.7.0 + resolution: "ultrahtml@npm:1.7.0" + checksum: 10c0/a56907b3ea4b0c16d30c37cb814138f8ae47044892a9d1c5ecca0731e24c2c4cd1c9cfa53d57bf508b21bc3b993e3c02bf3f9c3bfc156d18ed7e12c62a140f75 + languageName: node + linkType: hard + +"uncrypto@npm:^0.1.3": + version: 0.1.3 + resolution: "uncrypto@npm:0.1.3" + checksum: 10c0/74a29afefd76d5b77bedc983559ceb33f5bbc8dada84ff33755d1e3355da55a4e03a10e7ce717918c436b4dfafde1782e799ebaf2aadd775612b49f7b5b2998e + languageName: node + linkType: hard + +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 + languageName: node + linkType: hard + +"undici-types@npm:~7.18.0": + version: 7.18.2 + resolution: "undici-types@npm:7.18.2" + checksum: 10c0/85a79189113a238959d7a647368e4f7c5559c3a404ebdb8fc4488145ce9426fcd82252a844a302798dfc0e37e6fb178ff481ed03bc4caf634c5757d9ef43521d + languageName: node + linkType: hard + +"undici-types@npm:~8.9.0": + version: 8.9.0 + resolution: "undici-types@npm:8.9.0" + checksum: 10c0/51c712c20c087368f62c6fc0f2bc49fa76bfe5dec28ff01584bb677ff29b0622569783c02d43e151143808c48c0503f804093bd2c491b10f2a830138034cb451 + languageName: node + linkType: hard + +"undici@npm:^8.0.0, undici@npm:^8.4.1": + version: 8.10.2 + resolution: "undici@npm:8.10.2" + checksum: 10c0/66d7fc69149207cab570d6abbc9e965b6afe1ae889a7d46945b431165c973d6a56f3dd8b70a856e7ae1e550b6366a1cde6f06e68640587f0dadca38de72995ee + languageName: node + linkType: hard + +"unhead@npm:3.4.1, unhead@npm:^3.4.1": + version: 3.4.1 + resolution: "unhead@npm:3.4.1" + dependencies: + hookable: "npm:^6.1.1" + unplugin: "npm:^3.3.0" + peerDependencies: + vite: ">=6.4.2" + peerDependenciesMeta: + vite: + optional: true + checksum: 10c0/5385a540109943e508d9e92bdf54b8aca7f48af9e714c301fcf670db02dc92a1320f3f715a453f8f85298c5e105a47653f414835c135d4acaa95fc75e681acc7 + languageName: node + linkType: hard + +"unicorn-magic@npm:^0.1.0": + version: 0.1.0 + resolution: "unicorn-magic@npm:0.1.0" + checksum: 10c0/e4ed0de05b0a05e735c7d8a2930881e5efcfc3ec897204d5d33e7e6247f4c31eac92e383a15d9a6bccb7319b4271ee4bea946e211bf14951fec6ff2cbbb66a92 + languageName: node + linkType: hard + +"unified@npm:^11.0.5": + version: 11.0.5 + resolution: "unified@npm:11.0.5" + dependencies: + "@types/unist": "npm:^3.0.0" + bail: "npm:^2.0.0" + devlop: "npm:^1.0.0" + extend: "npm:^3.0.0" + is-plain-obj: "npm:^4.0.0" + trough: "npm:^2.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/53c8e685f56d11d9d458a43e0e74328a4d6386af51c8ac37a3dcabec74ce5026da21250590d4aff6733ccd7dc203116aae2b0769abc18cdf9639a54ae528dfc9 + languageName: node + linkType: hard + +"unifont@npm:~0.7.5": + version: 0.7.5 + resolution: "unifont@npm:0.7.5" + dependencies: + css-tree: "npm:^3.1.0" + ohash: "npm:^2.0.11" + undici: "npm:^8.0.0" + checksum: 10c0/bf91799b2fe53c8d360421f48cde7f56f2f14a9573cf49d10d97c9013efe50094a69258f3618108d899a202b36a2c627676fd901f61c2d6310e8ef2feed66047 + languageName: node + linkType: hard + +"unist-util-is@npm:^6.0.0": + version: 6.0.1 + resolution: "unist-util-is@npm:6.0.1" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 10c0/5a487d390193811d37a68264e204dbc7c15c40b8fc29b5515a535d921d071134f571d7b5cbd59bcd58d5ce1c0ab08f20fc4a1f0df2287a249c979267fc32ce06 + languageName: node + linkType: hard + +"unist-util-position@npm:^5.0.0": + version: 5.0.0 + resolution: "unist-util-position@npm:5.0.0" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 10c0/dde3b31e314c98f12b4dc6402f9722b2bf35e96a4f2d463233dd90d7cde2d4928074a7a11eff0a5eb1f4e200f27fc1557e0a64a7e8e4da6558542f251b1b7400 + languageName: node + linkType: hard + +"unist-util-stringify-position@npm:^4.0.0": + version: 4.0.0 + resolution: "unist-util-stringify-position@npm:4.0.0" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 10c0/dfe1dbe79ba31f589108cb35e523f14029b6675d741a79dea7e5f3d098785045d556d5650ec6a8338af11e9e78d2a30df12b1ee86529cded1098da3f17ee999e + languageName: node + linkType: hard + +"unist-util-visit-parents@npm:^6.0.0": + version: 6.0.2 + resolution: "unist-util-visit-parents@npm:6.0.2" + dependencies: + "@types/unist": "npm:^3.0.0" + unist-util-is: "npm:^6.0.0" + checksum: 10c0/f1e4019dbd930301825895e3737b1ee0cd682f7622ddd915062135cbb39f8c090aaece3a3b5eae1f2ea52ec33f0931abb8f8a8b5c48a511a4203e3d360a8cd49 + languageName: node + linkType: hard + +"unist-util-visit@npm:^5.0.0": + version: 5.1.0 + resolution: "unist-util-visit@npm:5.1.0" + dependencies: + "@types/unist": "npm:^3.0.0" + unist-util-is: "npm:^6.0.0" + unist-util-visit-parents: "npm:^6.0.0" + checksum: 10c0/a56e1bbbf63fcb55abe379e660b9a3367787e8be1e2473bdb7e86cfa6f32b6c1fa0092432d7040b8a30b2fc674bbbe024ffe6d03c3d6bf4839b064f584463a4e + languageName: node + linkType: hard + +"unplugin@npm:^1.3.1": + version: 1.16.1 + resolution: "unplugin@npm:1.16.1" + dependencies: + acorn: "npm:^8.14.0" + webpack-virtual-modules: "npm:^0.6.2" + checksum: 10c0/dd5f8c5727d0135847da73cf03fb199107f1acf458167034886fda3405737dab871ad3926431b4f70e1e82cdac482ac1383cea4019d782a68515c8e3e611b6cc + languageName: node + linkType: hard + +"unplugin@npm:^3.3.0": + version: 3.4.0 + resolution: "unplugin@npm:3.4.0" + dependencies: + "@jridgewell/remapping": "npm:^2.3.5" + picomatch: "npm:^4.0.7" + webpack-virtual-modules: "npm:^0.6.2" + peerDependencies: + "@farmfe/core": "*" + "@rsbuild/core": "*" + "@rspack/core": "*" + bun-types-no-globals: "*" + esbuild: "*" + rolldown: "*" + rollup: "*" + unloader: "*" + vite: "*" + webpack: "*" + peerDependenciesMeta: + "@farmfe/core": + optional: true + "@rsbuild/core": + optional: true + "@rspack/core": + optional: true + bun-types-no-globals: + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + unloader: + optional: true + vite: + optional: true + webpack: + optional: true + checksum: 10c0/d4674eec8c4093f42aa62736d68e85b4a366fcc45b8e910179f6f39f890d7b69c84179a769d3a6c23d41e658f89a2300f66474c126fee3f212330afedf48079a + languageName: node + linkType: hard + +"unstorage@npm:^1.17.5": + version: 1.17.5 + resolution: "unstorage@npm:1.17.5" + dependencies: + anymatch: "npm:^3.1.3" + chokidar: "npm:^5.0.0" + destr: "npm:^2.0.5" + h3: "npm:^1.15.10" + lru-cache: "npm:^11.2.7" + node-fetch-native: "npm:^1.6.7" + ofetch: "npm:^1.5.1" + ufo: "npm:^1.6.3" + peerDependencies: + "@azure/app-configuration": ^1.8.0 + "@azure/cosmos": ^4.2.0 + "@azure/data-tables": ^13.3.0 + "@azure/identity": ^4.6.0 + "@azure/keyvault-secrets": ^4.9.0 + "@azure/storage-blob": ^12.26.0 + "@capacitor/preferences": ^6 || ^7 || ^8 + "@deno/kv": ">=0.9.0" + "@netlify/blobs": ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + "@planetscale/database": ^1.19.0 + "@upstash/redis": ^1.34.3 + "@vercel/blob": ">=0.27.1" + "@vercel/functions": ^2.2.12 || ^3.0.0 + "@vercel/kv": ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: ">=0.2.1" + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + "@azure/app-configuration": + optional: true + "@azure/cosmos": + optional: true + "@azure/data-tables": + optional: true + "@azure/identity": + optional: true + "@azure/keyvault-secrets": + optional: true + "@azure/storage-blob": + optional: true + "@capacitor/preferences": + optional: true + "@deno/kv": + optional: true + "@netlify/blobs": + optional: true + "@planetscale/database": + optional: true + "@upstash/redis": + optional: true + "@vercel/blob": + optional: true + "@vercel/functions": + optional: true + "@vercel/kv": + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + checksum: 10c0/52bc07d0951129f493cc2d2a5204f49df90c85e7ab3faac0019e3a31f22f507f7a8263fca7ca4d019950c14cbfed4d43e15c9afe4b5dcb307249ebb60d2e538e + languageName: node + linkType: hard + +"update-browserslist-db@npm:^1.3.3": + version: 1.3.3 + resolution: "update-browserslist-db@npm:1.3.3" + dependencies: + escalade: "npm:^3.2.0" + picocolors: "npm:^1.1.1" + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 10c0/07bab166c18f47ba51f3b755e42f109ef5069d0d7c7224887decbd523d5ccd404f63661ebd5a740099972ed47f41f0e28c04df8be31d76716a346f948ce2aa61 + languageName: node + linkType: hard + +"use-callback-ref@npm:^1.3.3": + version: 1.3.3 + resolution: "use-callback-ref@npm:1.3.3" + dependencies: + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/f887488c6e6075cdad4962979da1714b217bcb1ee009a9e57ce9a844bcfc4c3a99e93983dfc2e5af9e0913824d24e730090ff255e902c516dcb58d2d3837e01c + languageName: node + linkType: hard + +"use-sidecar@npm:^1.1.3": + version: 1.1.3 + resolution: "use-sidecar@npm:1.1.3" + dependencies: + detect-node-es: "npm:^1.1.0" + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/161599bf921cfaa41c85d2b01c871975ee99260f3e874c2d41c05890d41170297bdcf314bc5185e7a700de2034ac5b888e3efc8e9f35724f4918f53538d717c9 + languageName: node + linkType: hard + +"verkit@npm:^0.4.0": + version: 0.4.1 + resolution: "verkit@npm:0.4.1" + checksum: 10c0/b184269beec1d8ee90915d504f3d3671663a9c840cae01e664ea2614fcd5ff7e42472ce0b2795b66a967abaef6dfe2ecf89c5d2a6f1826c8cab9e65a41b7fd0f + languageName: node + linkType: hard + +"vfile-message@npm:^4.0.0": + version: 4.0.3 + resolution: "vfile-message@npm:4.0.3" + dependencies: + "@types/unist": "npm:^3.0.0" + unist-util-stringify-position: "npm:^4.0.0" + checksum: 10c0/33d9f219610d27987689bb14fa5573d2daa146941d1a05416dd7702c4215b23f44ed81d059e70d0e4e24f9a57d5f4dc9f18d35a993f04cf9446a7abe6d72d0c0 + languageName: node + linkType: hard + +"vfile@npm:^6.0.0": + version: 6.0.3 + resolution: "vfile@npm:6.0.3" + dependencies: + "@types/unist": "npm:^3.0.0" + vfile-message: "npm:^4.0.0" + checksum: 10c0/e5d9eb4810623f23758cfc2205323e33552fb5972e5c2e6587babe08fe4d24859866277404fb9e2a20afb71013860d96ec806cb257536ae463c87d70022ab9ef + languageName: node + linkType: hard + +"vite@npm:^6.4.1": + version: 6.4.3 + resolution: "vite@npm:6.4.3" + dependencies: + esbuild: "npm:^0.25.0" + fdir: "npm:^6.4.4" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.2" + postcss: "npm:^8.5.3" + rollup: "npm:^4.34.9" + tinyglobby: "npm:^0.2.13" + peerDependencies: + "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: ">=1.21.0" + less: "*" + lightningcss: ^1.21.0 + sass: "*" + sass-embedded: "*" + stylus: "*" + sugarss: "*" + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/23ce22e50d5c7a87321f04b155c5bc3cf915e34e6c40918975741ed5e8b0c257039a643f1bc1cd829ee814ad92a138704e82084b7ebe40b399a62d8effea630c + languageName: node + linkType: hard + +"vite@npm:^7.1.0": + version: 7.3.6 + resolution: "vite@npm:7.3.6" + dependencies: + esbuild: "npm:^0.27.0 || ^0.28.0" + fdir: "npm:^6.5.0" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.3" + postcss: "npm:^8.5.6" + rollup: "npm:^4.43.0" + tinyglobby: "npm:^0.2.15" + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/c6d359f84ad362f5c97ff7988b77c90add04162c60cdf6059375a7d9e3217848c53a8cb6b6c48517bef84b6bba4c9bc85319a889b5236acb7d5a2bc8cb7b9a8d + languageName: node + linkType: hard + +"vite@npm:^8.0.13, vite@npm:^8.3.0": + version: 8.3.0 + resolution: "vite@npm:8.3.0" + dependencies: + fsevents: "npm:~2.3.3" + lightningcss: "npm:^1.33.0" + picomatch: "npm:^4.0.7" + postcss: "npm:^8.5.28" + rolldown: "npm:~1.2.6" + tinyglobby: "npm:^0.2.17" + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + "@vitejs/devtools": ^0.7.1 + esbuild: ^0.27.0 || ^0.28.0 + jiti: ">=1.21.0" + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + "@vitejs/devtools": + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/28d9a1c8b5018a865b8d8b2330859b11b035c3c578349d6ef0fc4a69b7a99460fc65aff75926d2ac18a7b217cd9a847901e2f7bcb32875f37319ccd81cd9064a + languageName: node + linkType: hard + +"vitefu@npm:^1.1.2": + version: 1.1.3 + resolution: "vitefu@npm:1.1.3" + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + checksum: 10c0/6cfcd52a17339f63f71ef08787a68850a17a9d9dd7a688e29e41067f963eb2cd5192bb65d38df87fa3a733a21e0229aaa6382201655c1599699e5d6669e3e094 + languageName: node + linkType: hard + +"volar-service-css@npm:0.0.71": + version: 0.0.71 + resolution: "volar-service-css@npm:0.0.71" + dependencies: + vscode-css-languageservice: "npm:^6.3.0" + vscode-languageserver-textdocument: "npm:^1.0.11" + vscode-uri: "npm:^3.0.8" + peerDependencies: + "@volar/language-service": ~2.4.0 + peerDependenciesMeta: + "@volar/language-service": + optional: true + checksum: 10c0/fd07e55786a18db2f671eea087e2d43116d8c0f504aac699a3b47fc9d6f53d66c0b3652062ca6ae08d8e59905564f72c63af9727d956165061849ab488462e2e + languageName: node + linkType: hard + +"volar-service-emmet@npm:0.0.71": + version: 0.0.71 + resolution: "volar-service-emmet@npm:0.0.71" + dependencies: + "@emmetio/css-parser": "npm:^0.4.1" + "@emmetio/html-matcher": "npm:^1.3.0" + "@vscode/emmet-helper": "npm:^2.9.3" + vscode-uri: "npm:^3.0.8" + peerDependencies: + "@volar/language-service": ~2.4.0 + peerDependenciesMeta: + "@volar/language-service": + optional: true + checksum: 10c0/a3c95991859d0e7e06a23d1201fcbdddbb7be0c525a553881f14ab941efe331adbb27431caf67962ec43a077e424f7e30bada6a52254272572b93b01f55bcf73 + languageName: node + linkType: hard + +"volar-service-html@npm:0.0.71": + version: 0.0.71 + resolution: "volar-service-html@npm:0.0.71" + dependencies: + vscode-html-languageservice: "npm:^5.3.0" + vscode-languageserver-textdocument: "npm:^1.0.11" + vscode-uri: "npm:^3.0.8" + peerDependencies: + "@volar/language-service": ~2.4.0 + peerDependenciesMeta: + "@volar/language-service": + optional: true + checksum: 10c0/b1ff293413945cf7357aadc9484080863e7a855a919ccc4f379123ebabe16f9905f5bd19db60e08028a7282c2926762a006daddabd0e373d5040d32afe2ab364 + languageName: node + linkType: hard + +"volar-service-prettier@npm:0.0.71": + version: 0.0.71 + resolution: "volar-service-prettier@npm:0.0.71" + dependencies: + vscode-uri: "npm:^3.0.8" + peerDependencies: + "@volar/language-service": ~2.4.0 + prettier: ^2.2 || ^3.0 + peerDependenciesMeta: + "@volar/language-service": + optional: true + prettier: + optional: true + checksum: 10c0/f3498260e51a5ee6d762fac0f332e745a5705db9ccd9aca9d154985cff733d22e8be6a41239bdce4cd5dcdaa05f8bd6e1baf27c5585f9b968e93155eb74e2118 + languageName: node + linkType: hard + +"volar-service-typescript-twoslash-queries@npm:0.0.71": + version: 0.0.71 + resolution: "volar-service-typescript-twoslash-queries@npm:0.0.71" + dependencies: + vscode-uri: "npm:^3.0.8" + peerDependencies: + "@volar/language-service": ~2.4.0 + peerDependenciesMeta: + "@volar/language-service": + optional: true + checksum: 10c0/deaea07b08c6dc54763c98217b1b206c885e4434a07018f058e22602f8445ee29ceac70217b1271b6182c979e8ccfe2fbdaa464e327ab8ae8a0eb87517d0adba + languageName: node + linkType: hard + +"volar-service-typescript@npm:0.0.71": + version: 0.0.71 + resolution: "volar-service-typescript@npm:0.0.71" + dependencies: + path-browserify: "npm:^1.0.1" + semver: "npm:^7.6.2" + typescript-auto-import-cache: "npm:^0.3.5" + vscode-languageserver-textdocument: "npm:^1.0.11" + vscode-nls: "npm:^5.2.0" + vscode-uri: "npm:^3.0.8" + peerDependencies: + "@volar/language-service": ~2.4.0 + peerDependenciesMeta: + "@volar/language-service": + optional: true + checksum: 10c0/c197666fca9810e72e9db81d15c527b21dbebd493966199b5e8d690204ba17cbc6cfaeed6be7d72b285bb795ff7fbb45a939463db0cb804360c4966e5811bbc0 + languageName: node + linkType: hard + +"volar-service-yaml@npm:0.0.71": + version: 0.0.71 + resolution: "volar-service-yaml@npm:0.0.71" + dependencies: + vscode-uri: "npm:^3.0.8" + yaml-language-server: "npm:~1.23.0" + peerDependencies: + "@volar/language-service": ~2.4.0 + peerDependenciesMeta: + "@volar/language-service": + optional: true + checksum: 10c0/6d56a6c77a100208e1ae0c5cbc80f547a066012a21574f476b37b59dfb212129efaf1a98fdc4cde8bcc71c61e9d26e0ac326d6c8ea300dffd681bf2c55b50f35 + languageName: node + linkType: hard + +"vscode-css-languageservice@npm:^6.3.0": + version: 6.3.10 + resolution: "vscode-css-languageservice@npm:6.3.10" + dependencies: + "@vscode/l10n": "npm:^0.0.18" + vscode-languageserver-textdocument: "npm:^1.0.12" + vscode-languageserver-types: "npm:3.17.5" + vscode-uri: "npm:^3.1.0" + checksum: 10c0/3f6228d85579c0306343c734716c17128757f3a6e44625deac06622fe248f05fe15da1901581aa50cb8d9539137a49d19f05dca6d13feb2e6bc9f3d9784236bd + languageName: node + linkType: hard + +"vscode-html-languageservice@npm:^5.3.0, vscode-html-languageservice@npm:^5.6.2": + version: 5.6.2 + resolution: "vscode-html-languageservice@npm:5.6.2" + dependencies: + "@vscode/l10n": "npm:^0.0.18" + vscode-languageserver-textdocument: "npm:^1.0.12" + vscode-languageserver-types: "npm:^3.17.5" + vscode-uri: "npm:^3.1.0" + checksum: 10c0/5ef5f3cb959435a3f29d9b5844fb223140b8b5b616c3f54d0c627427853e8c8806d5d93f3a4e3354a3c9d2e9acdf6cb8043479837d20d9dd30ad6d07bf22286d + languageName: node + linkType: hard + +"vscode-json-languageservice@npm:4.1.8": + version: 4.1.8 + resolution: "vscode-json-languageservice@npm:4.1.8" + dependencies: + jsonc-parser: "npm:^3.0.0" + vscode-languageserver-textdocument: "npm:^1.0.1" + vscode-languageserver-types: "npm:^3.16.0" + vscode-nls: "npm:^5.0.0" + vscode-uri: "npm:^3.0.2" + checksum: 10c0/9165703884e5eef52d4bf52294051df6623461cc7a3d458ef1f4fa3f98ac93c3a79efc8c4a15fda6ddf6e43d155c207c3c13534dcac3fe7982ddf1926dbf22a7 + languageName: node + linkType: hard + +"vscode-jsonrpc@npm:8.2.0": + version: 8.2.0 + resolution: "vscode-jsonrpc@npm:8.2.0" + checksum: 10c0/0789c227057a844f5ead55c84679206227a639b9fb76e881185053abc4e9848aa487245966cc2393fcb342c4541241b015a1a2559fddd20ac1e68945c95344e6 + languageName: node + linkType: hard + +"vscode-jsonrpc@npm:9.0.2": + version: 9.0.2 + resolution: "vscode-jsonrpc@npm:9.0.2" + checksum: 10c0/5eb4a902a4aa5b13655ea997dfdad6d5760df6acfd0388d7fccb641dff719af3ee1b9d99dc7548e21fe62e356f6b196c0bd37590f813137c7d9b6670fa22b237 + languageName: node + linkType: hard + +"vscode-languageserver-protocol@npm:3.17.5": + version: 3.17.5 + resolution: "vscode-languageserver-protocol@npm:3.17.5" + dependencies: + vscode-jsonrpc: "npm:8.2.0" + vscode-languageserver-types: "npm:3.17.5" + checksum: 10c0/5f38fd80da9868d706eaa4a025f4aff9c3faad34646bcde1426f915cbd8d7e8b6c3755ce3fef6eebd256ba3145426af1085305f8a76e34276d2e95aaf339a90b + languageName: node + linkType: hard + +"vscode-languageserver-protocol@npm:^3.17.5": + version: 3.18.3 + resolution: "vscode-languageserver-protocol@npm:3.18.3" + dependencies: + vscode-jsonrpc: "npm:9.0.2" + vscode-languageserver-types: "npm:3.18.3" + checksum: 10c0/4bff493ec34caf16eebdbfbc2baa4f35e909340344ff22d5165277b4d2a2221d821eec3bd1edb12403cdbb6f8c5af7eae0fbc9865f21ff93445f54a90e558306 + languageName: node + linkType: hard + +"vscode-languageserver-textdocument@npm:^1.0.1, vscode-languageserver-textdocument@npm:^1.0.11, vscode-languageserver-textdocument@npm:^1.0.12": + version: 1.0.14 + resolution: "vscode-languageserver-textdocument@npm:1.0.14" + checksum: 10c0/06a4c6cc82d1f84be6240fcbd48d50c536dfecb1c60e9e7eadcf6bad40c5a4de288f4f1262e09f2be007ec2c96f4acbf3ce3eb139bc0e7c28c16571d1e382f2f + languageName: node + linkType: hard + +"vscode-languageserver-types@npm:3.17.5": + version: 3.17.5 + resolution: "vscode-languageserver-types@npm:3.17.5" + checksum: 10c0/1e1260de79a2cc8de3e46f2e0182cdc94a7eddab487db5a3bd4ee716f67728e685852707d72c059721ce500447be9a46764a04f0611e94e4321ffa088eef36f8 + languageName: node + linkType: hard + +"vscode-languageserver-types@npm:3.18.3, vscode-languageserver-types@npm:^3.15.1, vscode-languageserver-types@npm:^3.16.0, vscode-languageserver-types@npm:^3.17.5": + version: 3.18.3 + resolution: "vscode-languageserver-types@npm:3.18.3" + checksum: 10c0/a750da5edbfb08a009b9fcfd780accff17c6312658eab7fcd29baf1bf8318578fdcd4de87a1e25754af7aec72aa5ee9218712614763f27e93313c85768ea17bf + languageName: node + linkType: hard + +"vscode-languageserver@npm:^9.0.0, vscode-languageserver@npm:^9.0.1": + version: 9.0.1 + resolution: "vscode-languageserver@npm:9.0.1" + dependencies: + vscode-languageserver-protocol: "npm:3.17.5" + bin: + installServerIntoExtension: bin/installServerIntoExtension + checksum: 10c0/8a0838d77c98a211c76e54bd3a6249fc877e4e1a73322673fb0e921168d8e91de4f170f1d4ff7e8b6289d0698207afc6aba6662d4c1cd8e4bd7cae96afd6b0c2 + languageName: node + linkType: hard + +"vscode-nls@npm:^5.0.0, vscode-nls@npm:^5.2.0": + version: 5.2.0 + resolution: "vscode-nls@npm:5.2.0" + checksum: 10c0/dc9e48f58ebbc807f435d351008813a2ea0c9432d51e778bcac9163c0642f929ddb518411ad654e775ce31e24d6acfa8fb7db8893c05b42c2019894e08b050f9 + languageName: node + linkType: hard + +"vscode-uri@npm:^3.0.2, vscode-uri@npm:^3.0.8, vscode-uri@npm:^3.1.0": + version: 3.2.0 + resolution: "vscode-uri@npm:3.2.0" + checksum: 10c0/65b4d78f2d014ee359ba36a81605bbd8a420d4c13d3fdba61b44b301ea416bf4b12637253ee72c0f30c1cd774c3649d667674c2fbf00c931667b869158f8519f + languageName: node + linkType: hard + +"web-streams-polyfill@npm:^3.0.3": + version: 3.3.3 + resolution: "web-streams-polyfill@npm:3.3.3" + checksum: 10c0/64e855c47f6c8330b5436147db1c75cb7e7474d924166800e8e2aab5eb6c76aac4981a84261dd2982b3e754490900b99791c80ae1407a9fa0dcff74f82ea3a7f + languageName: node + linkType: hard + +"webpack-virtual-modules@npm:^0.6.2": + version: 0.6.2 + resolution: "webpack-virtual-modules@npm:0.6.2" + checksum: 10c0/5ffbddf0e84bf1562ff86cf6fcf039c74edf09d78358a6904a09bbd4484e8bb6812dc385fe14330b715031892dcd8423f7a88278b57c9f5002c84c2860179add + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: "npm:^2.0.0" + bin: + node-which: ./bin/node-which + checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f + languageName: node + linkType: hard + +"which@npm:^4.0.0": + version: 4.0.0 + resolution: "which@npm:4.0.0" + dependencies: + isexe: "npm:^3.1.1" + bin: + node-which: bin/which.js + checksum: 10c0/449fa5c44ed120ccecfe18c433296a4978a7583bf2391c50abce13f76878d2476defde04d0f79db8165bdf432853c1f8389d0485ca6e8ebce3bbcded513d5e6a + languageName: node + linkType: hard + +"which@npm:^7.0.0": + version: 7.0.0 + resolution: "which@npm:7.0.0" + dependencies: + isexe: "npm:^4.0.0" + bin: + node-which: bin/which.js + checksum: 10c0/ca0b54f198f78bbc4b7c02e34bda8d335cb352e0adb4cbca1c37b1a957af3a879a82c4c27ca6525bc942f548d8b64f816ef6528360af9f3de55ffb9b979b620d + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 + languageName: node + linkType: hard + +"wrap-ansi@npm:^9.0.0": + version: 9.0.2 + resolution: "wrap-ansi@npm:9.0.2" + dependencies: + ansi-styles: "npm:^6.2.1" + string-width: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/3305839b9a0d6fb930cb63a52f34d3936013d8b0682ff3ec133c9826512620f213800ffa19ea22904876d5b7e9a3c1f40682f03597d986a4ca881fa7b033688c + languageName: node + linkType: hard + +"ws@npm:^8.13.0, ws@npm:^8.18.0": + version: 8.21.3 + resolution: "ws@npm:8.21.3" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/7b28dc2863ea0e2cece68d142a3eee90361021b73f750431e6d8076bb7dede5fdfdb75b3d29534b62f411261147b76b1bc80fb5cc63ab0aabd467280e85b22e0 + languageName: node + linkType: hard + +"xxhash-wasm@npm:^1.1.0": + version: 1.1.0 + resolution: "xxhash-wasm@npm:1.1.0" + checksum: 10c0/35aa152fc7d775ae13364fe4fb20ebd89c6ac1f56cdb6060a6d2f1ed68d15180694467e63a4adb3d11936a4798ccd75a540979070e70d9b911e9981bbdd9cea6 + languageName: node + linkType: hard + +"y18n@npm:^5.0.5": + version: 5.0.8 + resolution: "y18n@npm:5.0.8" + checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 + languageName: node + linkType: hard + +"yallist@npm:^3.0.2": + version: 3.1.1 + resolution: "yallist@npm:3.1.1" + checksum: 10c0/c66a5c46bc89af1625476f7f0f2ec3653c1a1791d2f9407cfb4c2ba812a1e1c9941416d71ba9719876530e3340a99925f697142989371b72d93b9ee628afd8c1 + languageName: node + linkType: hard + +"yallist@npm:^5.0.0": + version: 5.0.0 + resolution: "yallist@npm:5.0.0" + checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416 + languageName: node + linkType: hard + +"yaml-language-server@npm:~1.23.0": + version: 1.23.0 + resolution: "yaml-language-server@npm:1.23.0" + dependencies: + "@vscode/l10n": "npm:^0.0.18" + ajv: "npm:^8.17.1" + ajv-draft-04: "npm:^1.0.0" + ajv-i18n: "npm:^4.2.0" + prettier: "npm:^3.8.1" + request-light: "npm:^0.5.7" + vscode-json-languageservice: "npm:4.1.8" + vscode-languageserver: "npm:^9.0.0" + vscode-languageserver-textdocument: "npm:^1.0.1" + vscode-languageserver-types: "npm:^3.16.0" + vscode-uri: "npm:^3.0.2" + yaml: "npm:2.8.3" + bin: + yaml-language-server: bin/yaml-language-server + checksum: 10c0/d5ed44bb542ad9b9a38171983257f47c391120fb703e043395f3490c22d99f0807d90a0cdb7b4b7955f39557162936f41bd0d6f121c95b473bc1deac597c41a1 + languageName: node + linkType: hard + +"yaml@npm:2.8.3": + version: 2.8.3 + resolution: "yaml@npm:2.8.3" + bin: + yaml: bin.mjs + checksum: 10c0/ddff0e11c1b467728d7eb4633db61c5f5de3d8e9373cf84d08fb0cdee03e1f58f02b9f1c51a4a8a865751695addbd465a77f73f1079be91fe5493b29c305fd77 + languageName: node + linkType: hard + +"yaml@npm:^2.8.3": + version: 2.9.1 + resolution: "yaml@npm:2.9.1" + bin: + yaml: bin.mjs + checksum: 10c0/9d06676ef02b89559a8b10a71882bd2cdae81e8393dc8dbc871615c3259475d9609ad973d19cb3c9e71e72d6206e3d1e7aa241c7e55f3d16fd2e77db305827d6 + languageName: node + linkType: hard + +"yargs-parser@npm:^22.0.0": + version: 22.0.0 + resolution: "yargs-parser@npm:22.0.0" + checksum: 10c0/cb7ef81759c4271cb1d96b9351dbbc9a9ce35d3e1122d2b739bf6c432603824fa02c67cc12dcef6ea80283379d63495686e8f41cc7b06c6576e792aba4d33e1c + languageName: node + linkType: hard + +"yargs@npm:^18.0.0": + version: 18.2.0 + resolution: "yargs@npm:18.2.0" + dependencies: + cliui: "npm:^9.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + string-width: "npm:^8.2.1" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^22.0.0" + checksum: 10c0/7419338c3c00f9143ec6b6ab168335061c6f5342d161642aec23ff119e28cac1c417e0eca1d7dc82fd084f07207cc5d34c7d8315f605c0e3dbf12c350e405f3f + languageName: node + linkType: hard + +"yocto-queue@npm:^1.0.0, yocto-queue@npm:^1.2.1": + version: 1.2.2 + resolution: "yocto-queue@npm:1.2.2" + checksum: 10c0/36d4793e9cf7060f9da543baf67c55e354f4862c8d3d34de1a1b1d7c382d44171315cc54abf84d8900b8113d742b830108a1434f4898fb244f9b7e8426d4b8f5 + languageName: node + linkType: hard + +"zod@npm:^4.5.4, zod@npm:^4.6.5": + version: 4.6.5 + resolution: "zod@npm:4.6.5" + checksum: 10c0/32eb6c5e98becbe045675b2141c3611451c16ba567f9779231d79af41c4d651ca1c22f154aef7e6c6e77b929f23e43f1154ca05b82e119e922353725d6776af5 + languageName: node + linkType: hard + +"zwitch@npm:^2.0.0, zwitch@npm:^2.0.4": + version: 2.0.4 + resolution: "zwitch@npm:2.0.4" + checksum: 10c0/3c7830cdd3378667e058ffdb4cf2bb78ac5711214e2725900873accb23f3dfe5f9e7e5a06dcdc5f29605da976fc45c26d9a13ca334d6eea2245a15e77b8fc06e + languageName: node + linkType: hard