diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..49980cb
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,4 @@
+VITE_APP_ENV=development
+VITE_API_BASE_URL=http://localhost:3000
+VITE_ENABLE_QUERY_DEVTOOLS=true
+VITE_ENABLE_REQUEST_LOG=true
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..49980cb
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,4 @@
+VITE_APP_ENV=development
+VITE_API_BASE_URL=http://localhost:3000
+VITE_ENABLE_QUERY_DEVTOOLS=true
+VITE_ENABLE_REQUEST_LOG=true
diff --git a/.env.production b/.env.production
new file mode 100644
index 0000000..32fb8d6
--- /dev/null
+++ b/.env.production
@@ -0,0 +1,4 @@
+VITE_APP_ENV=production
+VITE_API_BASE_URL=https://api.example.com
+VITE_ENABLE_QUERY_DEVTOOLS=false
+VITE_ENABLE_REQUEST_LOG=false
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d5ec0f6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,27 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+coverage
+*.local
+.env.local
+.env.*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/.husky/commit-msg b/.husky/commit-msg
new file mode 100755
index 0000000..2e6b87e
--- /dev/null
+++ b/.husky/commit-msg
@@ -0,0 +1 @@
+pnpm exec commitlint --edit "$1"
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000..5ee7abd
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+pnpm exec lint-staged
diff --git a/README.md b/README.md
index 676dc4c..3fb190f 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,420 @@
-# 36-character-flower
+# React SPA Template
-36字花
\ No newline at end of file
+这是一个用于后台系统或通用 SPA 项目的前端基础脚手架。它已经接好了以下底座能力:
+
+- `Vite + React 19 + TypeScript`
+- `TanStack Router` 文件路由
+- `TanStack Query + ky` 请求与缓存
+- `Zustand` 会话状态基座
+- `Tailwind CSS` 原子化样式
+- `i18next` 多语言
+- `Head / Metadata` 动态标题与元信息
+- `Biome + Husky + commitlint` 代码规范与提交规范
+
+这个模板现在已经被“净化”为一个空白 scaffold。
+你拿到它之后,应该在这个基础上替换成自己的页面、接口、登录逻辑和测试,而不是继续沿用示例业务。
+
+## 1. 快速开始
+
+```bash
+pnpm install
+pnpm dev
+```
+
+常用命令:
+
+```bash
+pnpm dev
+pnpm build
+pnpm lint
+pnpm lint:fix
+pnpm generate-routes
+pnpm commit
+```
+
+## 2. 目录结构
+
+核心目录说明:
+
+- `src/routes`
+ TanStack Router 文件路由目录。你新增页面,优先在这里建文件。
+- `src/lib/api`
+ 通用请求层。`api-client.ts` 是所有接口调用的基础入口。
+- `src/lib/auth`
+ 会话初始化、401 清理、refresh token、受保护路由 helper。
+- `src/lib/head`
+ 页面标题、description、Open Graph、Twitter metadata。
+- `src/lib/query`
+ TanStack Query 全局默认配置。
+- `src/store`
+ 全局状态。目前主要是认证状态。
+- `src/styles.css`
+ 全局 Tailwind 入口。当前项目只保留这一份样式文件。
+- `src/locales`
+ 多语言文案。
+
+## 3. 新项目落地后先改哪里
+
+如果你要基于这个模板开一个新项目,建议按这个顺序改:
+
+### 第一步:改首页
+
+先改这个文件:
+
+- `src/routes/$lang/index.tsx`
+
+这是当前默认首页。通常你会:
+
+- 改成你的项目欢迎页
+- 或直接改成 dashboard / 工作台入口
+- 或拆成自己的页面布局
+
+同时对应修改多语言文案:
+
+- `src/locales/zh-CN/common.ts`
+- `src/locales/en-US/common.ts`
+
+### 第二步:改接口基础地址
+
+先看这些文件:
+
+- `.env.development`
+- `.env.production`
+- `src/vite-env.d.ts`
+
+至少确认这些变量:
+
+- `VITE_APP_ENV`
+- `VITE_API_BASE_URL`
+- `VITE_ENABLE_QUERY_DEVTOOLS`
+- `VITE_ENABLE_REQUEST_LOG`
+
+### 第三步:改全局样式入口
+
+当前模板已经切到 Tailwind CSS,并移除了旧的 `App.css` / `index.css`。
+
+你主要会改:
+
+- `src/styles.css`
+
+这里通常只放:
+
+- `@import "tailwindcss";`
+- 少量全局 base 样式
+- 你的主题变量
+
+### 第四步:接你的业务模块
+
+建议按“功能模块”建目录,而不是把所有请求、类型和 hooks 混在一起。
+
+例如:
+
+```text
+src/features/user/api/user-api.ts
+src/features/user/hooks/use-current-user.ts
+src/features/user/types/user.ts
+src/routes/$lang/users/index.tsx
+```
+
+### 第五步:接你的登录体系
+
+优先改这些文件:
+
+- `src/store/auth-store.ts`
+- `src/lib/auth/auth-session.ts`
+- `src/lib/api/api-client.ts`
+
+## 4. Head / Metadata 怎么用
+
+模板已经提供了一个通用 hook:
+
+- `src/lib/head/document-metadata.ts`
+
+在页面组件里直接调用:
+
+```tsx
+import { useDocumentMetadata } from '@/lib/head/document-metadata'
+
+function UserPage() {
+ useDocumentMetadata({
+ title: '用户管理',
+ description: '用户管理页面',
+ })
+
+ return
User Page
+}
+```
+
+它会自动更新:
+
+- `document.title`
+- `meta[name="description"]`
+- `meta[name="robots"]`
+- `og:title`
+- `og:description`
+- `twitter:title`
+- `twitter:description`
+
+适用场景:
+
+- 列表页标题
+- 详情页标题
+- 登录页 / 404 页
+- 分享卡片基础描述
+
+## 5. 登录怎么接
+
+这个模板的认证层不是“现成登录系统”,而是“认证骨架”。
+
+它已经有:
+
+- `accessToken`
+- `refreshToken`
+- `currentUser`
+- `status`
+- `401` 后清会话
+- refresh token 的可插拔入口
+
+核心文件:
+
+- `src/store/auth-store.ts`
+- `src/lib/auth/auth-session.ts`
+- `src/lib/api/api-client.ts`
+
+### 5.1 登录成功后怎么写入会话
+
+假设你的登录接口返回:
+
+```ts
+{
+ accessToken: string
+ refreshToken: string
+ user: {
+ id: string
+ name: string
+ }
+}
+```
+
+那么登录成功后可以这样写:
+
+```ts
+import { useAuthStore } from '@/store/auth-store'
+
+useAuthStore.getState().startSession({
+ accessToken: response.accessToken,
+ refreshToken: response.refreshToken,
+ currentUser: response.user,
+})
+```
+
+### 5.2 刷新 token 怎么接
+
+模板里预留了 refresh 的注册入口:
+
+```ts
+import { registerRefreshSessionHandler } from '@/lib/auth/auth-session'
+
+registerRefreshSessionHandler(async (refreshToken) => {
+ const response = await fetch('/auth/refresh', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ refreshToken }),
+ }).then((result) => result.json())
+
+ return {
+ accessToken: response.accessToken,
+ refreshToken: response.refreshToken,
+ currentUser: response.user ?? null,
+ }
+})
+```
+
+当请求返回 `401` 时,请求层会:
+
+1. 尝试执行这个 refresh handler
+2. 如果 refresh 成功,则自动重试原请求一次
+3. 如果 refresh 失败,则清空本地会话
+
+### 5.3 当前用户初始化怎么接
+
+如果你的项目在刷新页面后,需要根据已有 token 主动请求一次“当前用户信息”,可以注册:
+
+```ts
+import { registerCurrentUserInitializer } from '@/lib/auth/auth-session'
+
+registerCurrentUserInitializer(async () => {
+ const response = await fetch('/auth/me').then((result) => result.json())
+
+ return response.user
+})
+```
+
+这样在应用启动时,模板会在已有 token 且尚未拿到 `currentUser` 的情况下,自动初始化一次用户信息。
+
+## 6. 怎么加受保护路由
+
+模板已经提供了路由保护 helper:
+
+- `src/lib/auth/require-auth.ts`
+
+用法是,在路由的 `beforeLoad` 里调用它。
+
+示例:
+
+```tsx
+import { createFileRoute } from '@tanstack/react-router'
+
+import { requireAuthenticatedSession } from '@/lib/auth/require-auth'
+
+export const Route = createFileRoute('/$lang/dashboard')({
+ beforeLoad: async () => {
+ await requireAuthenticatedSession()
+ },
+ component: DashboardPage,
+})
+
+function DashboardPage() {
+ return Dashboard
+}
+```
+
+它的行为是:
+
+- 如果当前已经登录,正常进入页面
+- 如果当前未登录,重定向到 `/$lang`
+
+如果你想把未登录用户导向专门的登录页,可以直接修改:
+
+- `src/lib/auth/require-auth.ts`
+
+把重定向目标从 `/$lang` 改成你的登录页路由,例如 `/$lang/login`。
+
+## 7. 怎么写第一个接口模块
+
+推荐模式是:
+
+1. 在 `src/features/.../types` 定义类型
+2. 在 `src/features/.../api` 写请求函数
+3. 在 `src/features/.../hooks` 写 query / mutation hook
+4. 在 `src/routes/...` 页面里消费
+
+示例:
+
+```ts
+// src/features/user/api/user-api.ts
+import { api } from '@/lib/api/api-client'
+
+export interface CurrentUser {
+ id: string
+ name: string
+}
+
+export function getCurrentUser() {
+ return api.get('users/me').then((response) => response.data)
+}
+```
+
+```ts
+// src/features/user/hooks/use-current-user.ts
+import { useQuery } from '@tanstack/react-query'
+
+import { getCurrentUser } from '@/features/user/api/user-api'
+import type { ApiError } from '@/lib/api/api-error'
+
+export function useCurrentUserQuery() {
+ return useQuery>, ApiError>({
+ queryKey: ['current-user'],
+ queryFn: getCurrentUser,
+ })
+}
+```
+
+## 8. 路由相关说明
+
+这个模板使用 TanStack Router 文件路由。
+
+路由目录:
+
+- `src/routes`
+
+生成文件:
+
+- `src/routeTree.gen.ts`
+
+你通常不需要手改 `src/routeTree.gen.ts`。
+新增、删除、修改路由文件后,执行:
+
+```bash
+pnpm generate-routes
+```
+
+`pnpm dev` 和 `pnpm build` 也都会自动先生成一次路由树。
+
+## 9. 代码规范和提交流程
+
+## 9.1 Tailwind 说明
+
+当前模板使用的是官方 Vite 接法:
+
+- `tailwindcss`
+- `@tailwindcss/vite`
+
+核心文件:
+
+- `vite.config.ts`
+- `src/styles.css`
+
+新增页面时,优先直接写 Tailwind utility class,而不是再恢复成多个分散的 CSS 文件。
+
+格式化与 lint:
+
+```bash
+pnpm lint
+pnpm lint:fix
+pnpm format
+```
+
+提交规范:
+
+```bash
+pnpm commit
+```
+
+仓库已经包含:
+
+- `Biome`
+- `Husky`
+- `lint-staged`
+- `commitlint`
+- `Commitizen`
+
+## 10. 你后续最可能改动的文件
+
+如果你刚接手这个模板,优先关注这几个文件:
+
+- `src/routes/$lang/index.tsx`
+- `src/routes/$lang/route.tsx`
+- `src/lib/api/api-client.ts`
+- `src/lib/auth/auth-session.ts`
+- `src/lib/auth/require-auth.ts`
+- `src/store/auth-store.ts`
+- `src/lib/head/document-metadata.ts`
+- `src/locales/zh-CN/common.ts`
+- `src/locales/en-US/common.ts`
+
+## 11. 当前状态
+
+当前模板已经验证通过:
+
+- `pnpm lint`
+- `pnpm build`
+
+如果你继续往前整理,下一步通常就是:
+
+1. 接入真实登录接口
+2. 增加 `login`、`dashboard`、`403` 等实际页面
+3. 按业务模块拆分 `features`
+4. 按项目实际需要补充测试方案
diff --git a/biome.json b/biome.json
new file mode 100644
index 0000000..fafe1dd
--- /dev/null
+++ b/biome.json
@@ -0,0 +1,65 @@
+{
+ "$schema": "https://biomejs.dev/schemas/2.4.12/schema.json",
+ "vcs": {
+ "enabled": true,
+ "clientKind": "git",
+ "useIgnoreFile": true
+ },
+ "files": {
+ "ignoreUnknown": true,
+ "includes": ["**", "!coverage", "!src/routeTree.gen.ts"]
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "space",
+ "indentWidth": 2
+ },
+ "assist": {
+ "enabled": true,
+ "actions": {
+ "source": {
+ "organizeImports": "on"
+ }
+ }
+ },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "recommended": true,
+ "complexity": {
+ "noUselessTypeConstraint": "error"
+ },
+ "correctness": {
+ "useExhaustiveDependencies": "warn",
+ "useHookAtTopLevel": "error"
+ },
+ "style": {
+ "noNamespace": "error",
+ "useArrayLiterals": "error",
+ "useAsConstAssertion": "error",
+ "useComponentExportOnlyModules": {
+ "level": "error",
+ "options": {
+ "allowConstantExport": true
+ }
+ },
+ "useConst": "error"
+ },
+ "suspicious": {
+ "noExplicitAny": "error"
+ }
+ }
+ },
+ "javascript": {
+ "formatter": {
+ "quoteStyle": "single",
+ "jsxQuoteStyle": "double",
+ "semicolons": "asNeeded"
+ }
+ },
+ "css": {
+ "parser": {
+ "tailwindDirectives": true
+ }
+ }
+}
diff --git a/commitlint.config.mjs b/commitlint.config.mjs
new file mode 100644
index 0000000..d179c69
--- /dev/null
+++ b/commitlint.config.mjs
@@ -0,0 +1,3 @@
+export default {
+ extends: ['@commitlint/config-conventional'],
+}
diff --git a/docs/.~36字花_ 前端开发对接与交互逻辑说明书》.docx b/docs/.~36字花_ 前端开发对接与交互逻辑说明书》.docx
new file mode 100644
index 0000000..99b9bd3
Binary files /dev/null and b/docs/.~36字花_ 前端开发对接与交互逻辑说明书》.docx differ
diff --git a/docs/frontend-baseline-requirements.md b/docs/frontend-baseline-requirements.md
new file mode 100644
index 0000000..8dd8fc6
--- /dev/null
+++ b/docs/frontend-baseline-requirements.md
@@ -0,0 +1,439 @@
+# 《36字花》前端开发基线需求文档
+
+## 1. 文档目的
+
+本文件基于以下两份前期文档整理:
+
+- `docs/《_36字花_ 前端开发对接与交互逻辑说明书》.docx`
+- `docs/《36字花》用户前端 (User Portal) UI_UX .docx`
+
+目标是移除营销型表述和非关键修辞,只保留后续 UI 设计、前端开发、联调和验收所需的核心设计要求、交互逻辑和技术栈建议。
+
+## 2. 核心结论
+
+### 2.1 产品形态
+
+- 产品为实时开奖类游戏前端,主场景是移动端 H5,同时要求兼容桌面端响应式布局。
+- 页面核心是围绕单局倒计时循环运行的 36 宫格下注界面。
+- 前端必须以服务器状态为准,不能依赖客户端本地时间做开奖判断。
+
+### 2.2 设计方向
+
+- 视觉风格保留为深色背景、高对比强调色的娱乐场风格。
+- 颜色建议以深海蓝/黑为底,青色和金色作为强调色。
+- 视觉参考中的“神鼎、宝箱、卷轴、巨龙、赛博朋克”等描述,只作为美术方向参考,不作为开发阻塞项。
+- 所有文案容器必须支持多语言伸缩,禁止固定宽度写死。
+
+### 2.3 交付优先级
+
+后续设计与开发应按以下优先级执行:
+
+1. 玩法状态正确
+2. 倒计时与服务端同步正确
+3. 下单、封盘、开奖、派彩状态切换正确
+4. 移动端可用性与性能达标
+5. 动效和视觉强化
+
+## 3. 推荐技术栈
+
+## 3.1 前端框架
+
+- `React + TypeScript + Vite`
+- 原因:移动 H5 交互复杂、状态密集、联调频繁,Vite 启动快,React 生态成熟,TypeScript 适合管理复杂状态和接口约束。
+
+## 3.2 路由与页面组织
+
+- `React Router`
+- 页面结构建议按大厅主界面、公告弹窗、规则面板、用户侧滑面板拆分。
+
+## 3.3 状态管理
+
+- `Zustand` 作为全局业务状态容器
+- `XState` 或等价状态机方案用于维护单局生命周期状态
+- 原因:本项目存在明确的 4 段式回合状态机、局内局外状态切换、自动托管覆盖态、网络重连恢复,单纯依赖局部状态容易错乱。
+
+建议状态拆分:
+
+- `roundStore`:期号、阶段、倒计时、开奖数据
+- `betStore`:选中格子、锁定格子、筹码、总注额、按钮状态
+- `userStore`:余额、连赢状态、下注上限、公告状态
+- `uiStore`:弹窗、抽屉、Toast、动画开关、自动托管蒙层
+
+## 3.4 数据请求与实时通信
+
+- `TanStack Query`:管理普通 HTTP 请求、缓存与重拉
+- `WebSocket`:接收当前局状态、开奖、余额变动、派彩、自动托管进度
+- 如果后端使用 Socket.IO,则前端改用 `socket.io-client`
+
+## 3.5 样式与动画
+
+- 页面布局:`Tailwind CSS`
+- 复杂状态样式:`CSS Modules` 或同级方案
+- 盘面高频动画:优先 `CSS Animation / Transition / SVG`
+- 全屏粒子特效:`PixiJS` 或 `tsParticles`
+
+约束:
+
+- 禁止用 JS 逐帧操作 DOM 做 36 宫格边框动画
+- 动效必须优先使用合成层友好的属性,如 `transform`、`opacity`、`filter`
+
+## 3.6 国际化与时间处理
+
+- `react-i18next`
+- `dayjs`
+
+说明:
+
+- `dayjs` 只用于显示格式化,不用于决定回合状态。
+- 回合倒计时必须基于服务端时间戳推导。
+
+## 3.7 测试与质量保障
+
+- 单元测试:按项目需要自行接入
+- 组件测试:按项目需要自行接入
+- 端到端测试:`Playwright`
+- 线上监控:建议接入 `Sentry`
+
+## 4. 页面与模块范围
+
+## 4.1 主游戏界面
+
+必须包含以下区域:
+
+- 顶部导航栏
+- 开奖展示区
+- 中控信息区
+- 36 宫格下注区
+- 筹码与操作栏
+- 右侧历史区
+- 底部单双走势区
+
+## 4.2 次级界面
+
+必须包含以下弹层或侧栏:
+
+- 强制公告弹窗
+- 自动托管运行浮层
+- 玩法与规则面板
+- 用户 Dashboard 侧滑面板
+
+## 5. 单局生命周期状态机
+
+后台可配置单局时长,前端默认按 30 秒一局建模,并支持服务端参数覆盖。
+
+### 5.1 游戏阶段枚举
+
+- `BETTING`:下注期
+- `LOCKED`:封盘锁定
+- `DRAWING`:算票与开奖
+- `PAYOUT`:派彩与收尾
+
+### 5.2 各阶段规则
+
+`BETTING`
+
+- 允许选格子、换筹码、清除未确认、重复上一注、确认下注、开启自动托管。
+- 显示倒计时。
+- 跑马灯处于常规速度。
+
+`LOCKED`
+
+- 到达封盘时间点后,前端必须立即锁盘,不等待后端响应。
+- 所有点击事件、按钮、输入行为全部禁用。
+- 所有 `PRE_SELECTED` 未确认格子必须清空。
+- 显示“停止下注”提示。
+
+`DRAWING`
+
+- 等待 WebSocket 推送开奖结果。
+- 收到开奖后,盘面跑马灯加速。
+- 最终定格中奖格子,进入中奖态。
+
+`PAYOUT`
+
+- 接收余额和连赢状态更新。
+- 播放中奖特效。
+- 更新历史和走势。
+- 为下一局做状态重置。
+
+## 6. 36 宫格下注区需求
+
+## 6.1 网格结构
+
+- 固定为 `6 x 6`
+- 每个格子包含:编号、动物名、动物图
+- 格子需要支持轻量分组提示,用于区分业务分类,但不影响下注逻辑
+
+## 6.2 单格状态枚举
+
+建议统一实现 `CellStatus`:
+
+- `IDLE`:默认闲置
+- `MARQUEE`:跑马灯焦点
+- `HOVER`:PC 悬浮
+- `PRE_SELECTED`:已选未确认
+- `LOCKED`:已确认下注
+- `DISABLED`:不可操作
+- `ERROR`:错误反馈
+- `WINNING`:中奖高亮
+- `LOSER`:未中奖弱化
+- `AUTO_ACTIVE`:自动托管执行态
+
+## 6.3 单格交互规则
+
+- 玩家单局最多选择 5 个格子。
+- `PRE_SELECTED + LOCKED` 的总数不得超过 5。
+- 达到 5 个后,其余可选格子全部置为 `DISABLED`。
+- 点击第 6 个格子时,不得选中,必须触发错误反馈。
+- 余额不足或超限点击时,必须触发错误反馈。
+- 已确认格子在本局内不可取消。
+- 若本局尚未封盘且未满 5 个,允许继续追加下注并再次确认。
+
+## 7. 筹码与下注逻辑
+
+## 7.1 筹码区
+
+标准筹码档位:
+
+- `1`
+- `5`
+- `10`
+- `25`
+- `50`
+- `100`
+
+## 7.2 统一下注金额同步
+
+- 全局维护 `currentChipValue`
+- 已处于 `PRE_SELECTED` 的所有格子,其显示筹码必须跟随 `currentChipValue` 实时同步
+- 筹码切换后,总下注金额必须同步刷新
+
+## 7.3 连赢上限与余额限制
+
+需要实时校验:
+
+- `selectedCount * currentChipValue <= streakMaxBetLimit`
+- `totalBetAmount <= balance`
+
+当不满足时:
+
+- 对应不可选的大额筹码必须禁用
+- 确认下注按钮进入错误态或禁用态
+- 格子点击需给出明确错误反馈
+
+## 8. 确认下注主按钮状态机
+
+按钮需要独立维护以下 4 个状态:
+
+- `DISABLED`:未选任何格子
+- `READY`:已选格子且余额足够,可点击提交
+- `ERROR`:总下注金额大于余额,文案显示“余额不足”
+- `SUCCESS`:下注成功后维持成功态直到本局结束
+
+约束:
+
+- 禁止自动提交
+- 必须由用户手动点击确认
+- 成功后已确认格子不可撤销
+
+## 9. 自动托管需求
+
+## 9.1 功能行为
+
+- 调用自动托管接口提交:下注格子、金额、局数
+- 前端进入 `AUTO_MODE`
+- 主键盘和筹码区整体进入不可编辑状态
+- 自动托管中的目标格子显示 `AUTO_ACTIVE`
+- 前端展示当前进度,例如 `12 / 50`
+- 必须提供显式“停止托管”操作
+
+## 9.2 视觉与交互约束
+
+- 使用全局玻璃遮罩阻断手动操作
+- 自动托管目标格子需要穿透遮罩高亮显示
+- 自动托管态必须与手动锁定态有视觉区分
+
+## 10. 中控信息区需求
+
+必须展示以下信息:
+
+- 当前余额
+- 当前赔率
+- 当前连赢次数
+- 连赢限额提示
+- 当前倒计时
+- 当前期号
+- 当前阶段状态,如 `OPEN` / `CLOSED`
+
+倒计时要求:
+
+- 最后 5 秒需要强化提示
+- 倒计时只展示服务器推导结果
+
+## 11. 历史与走势需求
+
+## 11.1 右侧历史区
+
+- 显示最近开奖记录
+- 每条至少包含时间、号码、动物名
+- 最新一条高亮
+- 需要支持滚动
+
+## 11.2 底部单双走势
+
+- 保留最近 30 局
+- 奇数显示红色圆点
+- 偶数显示蓝色圆点
+- 新增一条数据时,最后一个点需要入场动画
+
+## 12. 弹窗与侧栏需求
+
+## 12.1 强制公告弹窗
+
+功能要求:
+
+- 首屏进入时请求公告接口
+- 若存在未读公告,必须强制弹出
+- 不允许点击遮罩关闭
+- 不允许提供右上角关闭按钮
+- 勾选“已阅读并同意”前,进入游戏按钮必须禁用
+- 关闭后要记录已读状态
+- 无新公告时,不重复弹出
+
+布局要求:
+
+- 支持图文内容
+- 内容超长时支持滚动
+- 移动端建议复选框和主按钮上下排列,避免多语言挤压
+
+## 12.2 规则面板
+
+- 提供玩法规则、赔率说明、连胜机制、大奖说明
+- 允许分页或滚动
+- 结构化展示,不得纯长文堆叠
+
+## 12.3 用户 Dashboard 侧滑面板
+
+至少包含:
+
+- 资产信息
+- 充值入口
+- 提现入口与手续费说明
+- 最近 1 个月投注历史
+- 站内信列表
+- 公告信箱入口
+
+## 13. 异常与容错要求
+
+## 13.1 本地锁盘优先
+
+- 到达封盘时间点时,前端必须立刻锁盘
+- 即使网络延迟,也不能继续允许下注交互
+
+## 13.2 压秒点击失败处理
+
+场景:
+
+- 用户在接近封盘时点击确认
+- 请求发出,但服务器实际已封盘
+
+前端处理:
+
+- 先进入锁盘和加载态
+- 若稍后收到失败响应,必须撤销未成功下注的本地状态
+- 明确提示“网络延迟,下注失败,未扣款”
+- 严禁误显示为下注成功
+
+## 13.3 断线重连恢复
+
+触发条件:
+
+- 页面重新可见
+- WebSocket 断开后重连
+
+前端处理:
+
+- 立即调用全量状态接口重新同步
+- 重置倒计时、期号、余额、连赢、走势、当前盘面状态
+- 禁止依赖本地累计时间继续运行
+
+## 13.4 余额不足场景
+
+- 确认按钮进入错误态
+- 充值入口需要有明显引导
+- 相关格子或筹码点击时给出即时反馈
+
+## 14. 接口与事件依赖
+
+以下为前端开发所需的最小接口能力,命名可与后端协商,但能力不可缺失。
+
+## 14.1 HTTP 接口
+
+- `GET /api/user/announcement`
+- `GET /api/game/current_status`
+- `POST /api/bet/place`
+- `POST /api/auto_spin`
+- `POST /api/announcement/read`
+
+## 14.2 WebSocket 事件
+
+- `round_status`:当前阶段、期号、服务器时间、剩余时间
+- `draw_result`:开奖结果、中奖格子
+- `balance_changed`:余额变化
+- `streak_changed`:连赢状态与限额变化
+- `trend_updated`:最新走势数据
+- `auto_spin_progress`:自动托管局数进度
+
+## 15. 性能与实现约束
+
+- 移动端目标帧率:`60 FPS`
+- 36 宫格状态切换不得出现明显掉帧
+- 高亮、缩放、呼吸、闪烁等动画优先使用 GPU 友好属性
+- 避免大面积重排和重绘
+- 长列表区域应考虑虚拟化或分段渲染
+- 全局状态更新必须避免引起整盘 36 格不必要重渲染
+
+## 16. 多语言与响应式要求
+
+- 所有按钮、标签、提示文案必须支持长度扩展
+- 不允许固定像素宽度导致文案截断
+- 文本区域需预留至少 40% 的横向伸缩空间
+- 设计需优先保证移动端单手操作
+- PC 端可补充 Hover 态,移动端不依赖 Hover 完成交互
+
+## 17. 开发验收基线
+
+满足以下条件才可进入测试或交付:
+
+- 单局状态机完整跑通
+- 封盘时前端能本地立即锁盘
+- 服务端时间同步准确
+- 下注数量限制与统一筹码机制无误
+- 余额与连赢上限限制生效
+- 自动托管可启动、运行、停止
+- 公告弹窗强阻断逻辑正确
+- 断线重连后状态可恢复
+- 走势与历史区数据更新正确
+- 移动端核心流程可稳定使用
+
+## 18. 建议的开发顺序
+
+1. 搭建项目骨架、路由、状态层、接口层
+2. 先实现 30 秒回合状态机和服务端时间同步
+3. 完成 36 宫格、筹码区、确认按钮的核心下注流程
+4. 接入开奖、派彩、历史、走势
+5. 实现公告弹窗、规则面板、用户侧栏
+6. 实现自动托管
+7. 最后补齐粒子特效、强化动画和视觉细节
+
+## 19. 明确降级为“视觉参考”的内容
+
+以下内容不应阻塞前端逻辑开发,可在视觉设计阶段再细化:
+
+- 神坛主体到底是神鼎、宝箱还是法槌
+- 是否采用卷轴、金属边框或全息科技框
+- Jackpot 动画具体表现形式
+- 动物头像是否 3D、半写实或插画风
+- 特效音、粒子素材、品牌化图标样式
+
+以上内容只影响视觉表现,不影响本文件定义的交互和功能边界。
diff --git a/docs/《36字花》用户前端 (User Portal) UI_UX .docx b/docs/《36字花》用户前端 (User Portal) UI_UX .docx
new file mode 100644
index 0000000..64ebf8c
Binary files /dev/null and b/docs/《36字花》用户前端 (User Portal) UI_UX .docx differ
diff --git a/docs/《_36字花_ 前端开发对接与交互逻辑说明书》.docx b/docs/《_36字花_ 前端开发对接与交互逻辑说明书》.docx
new file mode 100644
index 0000000..ebe8a02
Binary files /dev/null and b/docs/《_36字花_ 前端开发对接与交互逻辑说明书》.docx differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..bb56a27
--- /dev/null
+++ b/index.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ React SPA Template
+
+
+
+
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..4ee3e0c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,61 @@
+{
+ "name": "react-spa-template",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "commit": "cz",
+ "dev": "pnpm generate-routes && vite",
+ "build": "pnpm generate-routes && tsc -b && vite build",
+ "generate-routes": "tsr generate",
+ "lint": "biome check .",
+ "lint:fix": "biome check --write .",
+ "format": "biome format --write .",
+ "prepare": "husky",
+ "preview": "vite preview"
+ },
+ "lint-staged": {
+ "*.{js,jsx,ts,tsx,json,jsonc,css,md,html}": [
+ "biome check --write"
+ ]
+ },
+ "config": {
+ "commitizen": {
+ "path": "cz-conventional-changelog"
+ }
+ },
+ "dependencies": {
+ "@tanstack/react-query": "^5.99.0",
+ "@tanstack/react-query-devtools": "^5.99.0",
+ "@tanstack/react-router": "^1.168.22",
+ "i18next": "^26.0.5",
+ "ky": "^2.0.1",
+ "react": "^19.2.4",
+ "react-dom": "^19.2.4",
+ "react-i18next": "^17.0.3",
+ "zustand": "^5.0.12"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.29.0",
+ "@biomejs/biome": "2.4.12",
+ "@commitlint/cli": "20.5.0",
+ "@commitlint/config-conventional": "20.5.0",
+ "@rolldown/plugin-babel": "^0.2.2",
+ "@tailwindcss/vite": "^4.2.2",
+ "@tanstack/router-cli": "^1.166.33",
+ "@tanstack/router-plugin": "^1.167.22",
+ "@types/babel__core": "^7.20.5",
+ "@types/node": "^24.12.2",
+ "@types/react": "^19.2.14",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.1",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "commitizen": "4.3.1",
+ "cz-conventional-changelog": "3.3.0",
+ "husky": "9.1.7",
+ "lint-staged": "16.4.0",
+ "tailwindcss": "^4.2.2",
+ "typescript": "~6.0.2",
+ "vite": "^8.0.4"
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..7d4d468
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,3687 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@tanstack/react-query':
+ specifier: ^5.99.0
+ version: 5.99.0(react@19.2.5)
+ '@tanstack/react-query-devtools':
+ specifier: ^5.99.0
+ version: 5.99.0(@tanstack/react-query@5.99.0(react@19.2.5))(react@19.2.5)
+ '@tanstack/react-router':
+ specifier: ^1.168.22
+ version: 1.168.22(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
+ i18next:
+ specifier: ^26.0.5
+ version: 26.0.5(typescript@6.0.2)
+ ky:
+ specifier: ^2.0.1
+ version: 2.0.1
+ react:
+ specifier: ^19.2.4
+ version: 19.2.5
+ react-dom:
+ specifier: ^19.2.4
+ version: 19.2.5(react@19.2.5)
+ react-i18next:
+ specifier: ^17.0.3
+ version: 17.0.3(i18next@26.0.5(typescript@6.0.2))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@6.0.2)
+ zustand:
+ specifier: ^5.0.12
+ version: 5.0.12(@types/react@19.2.14)(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5))
+ devDependencies:
+ '@babel/core':
+ specifier: ^7.29.0
+ version: 7.29.0
+ '@biomejs/biome':
+ specifier: 2.4.12
+ version: 2.4.12
+ '@commitlint/cli':
+ specifier: 20.5.0
+ version: 20.5.0(@types/node@24.12.2)(conventional-commits-parser@6.4.0)(typescript@6.0.2)
+ '@commitlint/config-conventional':
+ specifier: 20.5.0
+ version: 20.5.0
+ '@rolldown/plugin-babel':
+ specifier: ^0.2.2
+ version: 0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.0-rc.15)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))
+ '@tailwindcss/vite':
+ specifier: ^4.2.2
+ version: 4.2.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))
+ '@tanstack/router-cli':
+ specifier: ^1.166.33
+ version: 1.166.33
+ '@tanstack/router-plugin':
+ specifier: ^1.167.22
+ version: 1.167.22(@tanstack/react-router@1.168.22(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))
+ '@types/babel__core':
+ specifier: ^7.20.5
+ version: 7.20.5
+ '@types/node':
+ specifier: ^24.12.2
+ version: 24.12.2
+ '@types/react':
+ specifier: ^19.2.14
+ version: 19.2.14
+ '@types/react-dom':
+ specifier: ^19.2.3
+ version: 19.2.3(@types/react@19.2.14)
+ '@vitejs/plugin-react':
+ specifier: ^6.0.1
+ version: 6.0.1(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.0-rc.15)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)))(babel-plugin-react-compiler@1.0.0)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))
+ babel-plugin-react-compiler:
+ specifier: ^1.0.0
+ version: 1.0.0
+ commitizen:
+ specifier: 4.3.1
+ version: 4.3.1(@types/node@24.12.2)(typescript@6.0.2)
+ cz-conventional-changelog:
+ specifier: 3.3.0
+ version: 3.3.0(@types/node@24.12.2)(typescript@6.0.2)
+ husky:
+ specifier: 9.1.7
+ version: 9.1.7
+ lint-staged:
+ specifier: 16.4.0
+ version: 16.4.0
+ tailwindcss:
+ specifier: ^4.2.2
+ version: 4.2.2
+ typescript:
+ specifier: ~6.0.2
+ version: 6.0.2
+ vite:
+ specifier: ^8.0.4
+ version: 8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)
+
+packages:
+
+ '@babel/code-frame@7.29.0':
+ resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.29.0':
+ resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.29.0':
+ resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.29.1':
+ resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.28.6':
+ resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.28.6':
+ resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.6':
+ resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.28.6':
+ resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.29.2':
+ resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.29.2':
+ resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-syntax-jsx@7.28.6':
+ resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-typescript@7.28.6':
+ resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/runtime@7.29.2':
+ resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/template@7.28.6':
+ resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.29.0':
+ resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.29.0':
+ resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
+ engines: {node: '>=6.9.0'}
+
+ '@biomejs/biome@2.4.12':
+ resolution: {integrity: sha512-Rro7adQl3NLq/zJCIL98eElXKI8eEiBtoeu5TbXF/U3qbjuSc7Jb5rjUbeHHcquDWeSf3HnGP7XI5qGrlRk/pA==}
+ engines: {node: '>=14.21.3'}
+ hasBin: true
+
+ '@biomejs/cli-darwin-arm64@2.4.12':
+ resolution: {integrity: sha512-BnMU4Pc3ciEVteVpZ0BK33MLr7X57F5w1dwDLDn+/iy/yTrA4Q/N2yftidFtsA4vrDh0FMXDpacNV/Tl3fbmng==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@biomejs/cli-darwin-x64@2.4.12':
+ resolution: {integrity: sha512-x9uJ0bI1rJsWICp3VH8w/5PnAVD3A7SqzDpbrfoUQX1QyWrK5jSU4fRLo/wSgGeplCivbxBRKmt5Xq4/nWvq8A==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@biomejs/cli-linux-arm64-musl@2.4.12':
+ resolution: {integrity: sha512-FhfpkAAlKL6kwvcVap0Hgp4AhZmtd3YImg0kK1jd7C/aSoh4SfsB2f++yG1rU0lr8Y5MCFJrcSkmssiL9Xnnig==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@biomejs/cli-linux-arm64@2.4.12':
+ resolution: {integrity: sha512-tOwuCuZZtKi1jVzbk/5nXmIsziOB6yqN8c9r9QM0EJYPU6DpQWf11uBOSCfFKKM4H3d9ZoarvlgMfbcuD051Pw==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@biomejs/cli-linux-x64-musl@2.4.12':
+ resolution: {integrity: sha512-dwTIgZrGutzhkQCuvHynCkyW6hJxUuyZqKKO0YNfaS2GUoRO+tOvxXZqZB6SkWAOdfZTzwaw8IEdUnIkHKHoew==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@biomejs/cli-linux-x64@2.4.12':
+ resolution: {integrity: sha512-8pFeAnLU9QdW9jCIslB/v82bI0lhBmz2ZAKc8pVMFPO0t0wAHsoEkrUQUbMkIorTRIjbqyNZHA3lEXavsPWYSw==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@biomejs/cli-win32-arm64@2.4.12':
+ resolution: {integrity: sha512-B0DLnx0vA9ya/3v7XyCaP+/lCpnbWbMOfUFFve+xb5OxyYvdHaS55YsSddr228Y+JAFk58agCuZTsqNiw2a6ig==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@biomejs/cli-win32-x64@2.4.12':
+ resolution: {integrity: sha512-yMckRzTyZ83hkk8iDFWswqSdU8tvZxspJKnYNh7JZr/zhZNOlzH13k4ecboU6MurKExCe2HUkH75pGI/O2JwGA==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [win32]
+
+ '@commitlint/cli@20.5.0':
+ resolution: {integrity: sha512-yNkyN/tuKTJS3wdVfsZ2tXDM4G4Gi7z+jW54Cki8N8tZqwKBltbIvUUrSbT4hz1bhW/h0CdR+5sCSpXD+wMKaQ==}
+ engines: {node: '>=v18'}
+ hasBin: true
+
+ '@commitlint/config-conventional@20.5.0':
+ resolution: {integrity: sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/config-validator@20.5.0':
+ resolution: {integrity: sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/ensure@20.5.0':
+ resolution: {integrity: sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/execute-rule@20.0.0':
+ resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/format@20.5.0':
+ resolution: {integrity: sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/is-ignored@20.5.0':
+ resolution: {integrity: sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/lint@20.5.0':
+ resolution: {integrity: sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/load@20.5.0':
+ resolution: {integrity: sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/message@20.4.3':
+ resolution: {integrity: sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/parse@20.5.0':
+ resolution: {integrity: sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/read@20.5.0':
+ resolution: {integrity: sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/resolve-extends@20.5.0':
+ resolution: {integrity: sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/rules@20.5.0':
+ resolution: {integrity: sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/to-lines@20.0.0':
+ resolution: {integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/top-level@20.4.3':
+ resolution: {integrity: sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/types@20.5.0':
+ resolution: {integrity: sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==}
+ engines: {node: '>=v18'}
+
+ '@conventional-changelog/git-client@2.7.0':
+ resolution: {integrity: sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ conventional-commits-filter: ^5.0.0
+ conventional-commits-parser: ^6.4.0
+ peerDependenciesMeta:
+ conventional-commits-filter:
+ optional: true
+ conventional-commits-parser:
+ optional: true
+
+ '@emnapi/core@1.9.2':
+ resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==}
+
+ '@emnapi/runtime@1.9.2':
+ resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==}
+
+ '@emnapi/wasi-threads@1.2.1':
+ resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
+
+ '@esbuild/aix-ppc64@0.27.7':
+ resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.27.7':
+ resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.27.7':
+ resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.27.7':
+ resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.27.7':
+ resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.27.7':
+ resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.27.7':
+ resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.27.7':
+ resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.27.7':
+ resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.27.7':
+ resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.27.7':
+ resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.27.7':
+ resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.27.7':
+ resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.27.7':
+ resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.27.7':
+ resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.27.7':
+ resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.27.7':
+ resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.27.7':
+ resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.27.7':
+ resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.27.7':
+ resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.27.7':
+ resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.27.7':
+ resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.27.7':
+ resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.27.7':
+ resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.27.7':
+ resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.27.7':
+ resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@napi-rs/wasm-runtime@1.1.4':
+ resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==}
+ peerDependencies:
+ '@emnapi/core': ^1.7.1
+ '@emnapi/runtime': ^1.7.1
+
+ '@oxc-project/types@0.124.0':
+ resolution: {integrity: sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==}
+
+ '@rolldown/binding-android-arm64@1.0.0-rc.15':
+ resolution: {integrity: sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@rolldown/binding-darwin-arm64@1.0.0-rc.15':
+ resolution: {integrity: sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rolldown/binding-darwin-x64@1.0.0-rc.15':
+ resolution: {integrity: sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rolldown/binding-freebsd-x64@1.0.0-rc.15':
+ resolution: {integrity: sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15':
+ resolution: {integrity: sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15':
+ resolution: {integrity: sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-arm64-musl@1.0.0-rc.15':
+ resolution: {integrity: sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15':
+ resolution: {integrity: sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15':
+ resolution: {integrity: sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-x64-gnu@1.0.0-rc.15':
+ resolution: {integrity: sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-x64-musl@1.0.0-rc.15':
+ resolution: {integrity: sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-openharmony-arm64@1.0.0-rc.15':
+ resolution: {integrity: sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rolldown/binding-wasm32-wasi@1.0.0-rc.15':
+ resolution: {integrity: sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15':
+ resolution: {integrity: sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rolldown/binding-win32-x64-msvc@1.0.0-rc.15':
+ resolution: {integrity: sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@rolldown/plugin-babel@0.2.3':
+ resolution: {integrity: sha512-+zEk16yGlz1F9STiRr6uG9hmIXb6nprjLczV/htGptYuLoCuxb+itZ03RKCEeOhBpDDd1NU7qF6x1VLMUp62bw==}
+ engines: {node: '>=22.12.0 || ^24.0.0'}
+ peerDependencies:
+ '@babel/core': ^7.29.0 || ^8.0.0-rc.1
+ '@babel/plugin-transform-runtime': ^7.29.0 || ^8.0.0-rc.1
+ '@babel/runtime': ^7.27.0 || ^8.0.0-rc.1
+ rolldown: ^1.0.0-rc.5
+ vite: ^8.0.0
+ peerDependenciesMeta:
+ '@babel/plugin-transform-runtime':
+ optional: true
+ '@babel/runtime':
+ optional: true
+ vite:
+ optional: true
+
+ '@rolldown/pluginutils@1.0.0-rc.15':
+ resolution: {integrity: sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==}
+
+ '@rolldown/pluginutils@1.0.0-rc.7':
+ resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==}
+
+ '@simple-libs/child-process-utils@1.0.2':
+ resolution: {integrity: sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==}
+ engines: {node: '>=18'}
+
+ '@simple-libs/stream-utils@1.2.0':
+ resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==}
+ engines: {node: '>=18'}
+
+ '@tailwindcss/node@4.2.2':
+ resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==}
+
+ '@tailwindcss/oxide-android-arm64@4.2.2':
+ resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.2.2':
+ resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.2.2':
+ resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.2.2':
+ resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2':
+ resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.2.2':
+ resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.2.2':
+ resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.2.2':
+ resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.2.2':
+ resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.2.2':
+ resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.2.2':
+ resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.2.2':
+ resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.2.2':
+ resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==}
+ engines: {node: '>= 20'}
+
+ '@tailwindcss/vite@4.2.2':
+ resolution: {integrity: sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6 || ^7 || ^8
+
+ '@tanstack/history@1.161.6':
+ resolution: {integrity: sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg==}
+ engines: {node: '>=20.19'}
+
+ '@tanstack/query-core@5.99.0':
+ resolution: {integrity: sha512-3Jv3WQG0BCcH7G+7lf/bP8QyBfJOXeY+T08Rin3GZ1bshvwlbPt7NrDHMEzGdKIOmOzvIQmxjk28YEQX60k7pQ==}
+
+ '@tanstack/query-devtools@5.99.0':
+ resolution: {integrity: sha512-m4ufXaJ8FjWXw7xDtyzE/6fkZAyQFg9WrbMrUpt8ZecRJx58jiFOZ2lxZMphZdIpAnIeto/S8stbwLKLusyckQ==}
+
+ '@tanstack/react-query-devtools@5.99.0':
+ resolution: {integrity: sha512-CqqX7LCU9yOfCY/vBURSx2YSD83ryfX+QkfkaKionTfg1s2Hdm572Ro99gW3QPoJjzvsj1HM4pnN4nbDy3MXKA==}
+ peerDependencies:
+ '@tanstack/react-query': ^5.99.0
+ react: ^18 || ^19
+
+ '@tanstack/react-query@5.99.0':
+ resolution: {integrity: sha512-OY2bCqPemT1LlqJ8Y2CUau4KELnIhhG9Ol3ZndPbdnB095pRbPo1cHuXTndg8iIwtoHTgwZjyaDnQ0xD0mYwAw==}
+ peerDependencies:
+ react: ^18 || ^19
+
+ '@tanstack/react-router@1.168.22':
+ resolution: {integrity: sha512-W2LyfkfJtDCf//jOjZeUBWwOVl8iDRVTECpGHa2M28MT3T5/VVnjgicYNHR/ax0Filk1iU67MRjcjHheTYvK1Q==}
+ engines: {node: '>=20.19'}
+ peerDependencies:
+ react: '>=18.0.0 || >=19.0.0'
+ react-dom: '>=18.0.0 || >=19.0.0'
+
+ '@tanstack/react-store@0.9.3':
+ resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ '@tanstack/router-cli@1.166.33':
+ resolution: {integrity: sha512-gCWBbCVkfT2OzgxQVV275BjRYKvfh7SEKD73ATHWyLE8ifm8/O2700roObVHUy+Y0jJT91Am0UkjsES0O2jqzw==}
+ engines: {node: '>=20.19'}
+ hasBin: true
+
+ '@tanstack/router-core@1.168.15':
+ resolution: {integrity: sha512-Wr0424NDtD8fT/uALobMZ9DdcfsTyXtW5IPR++7zvW8/7RaIOeaqXpVDId8ywaGtqPWLWOfaUg2zUtYtukoXYA==}
+ engines: {node: '>=20.19'}
+ hasBin: true
+
+ '@tanstack/router-generator@1.166.32':
+ resolution: {integrity: sha512-VuusKwEXcgKq+myq1JQfZogY8scTXIIeFls50dJ/UXgCXWp5n14iFreYNlg41wURcak2oA3M+t2TVfD0xUUD6g==}
+ engines: {node: '>=20.19'}
+
+ '@tanstack/router-plugin@1.167.22':
+ resolution: {integrity: sha512-wYPzIvBK8bcmXVUpZfSgGBXOrfBAdF4odKevz6rejio5rEd947NtKDF5R7eYdwlAOmRqYpLJnJ1QHkc5t8bY4w==}
+ engines: {node: '>=20.19'}
+ hasBin: true
+ peerDependencies:
+ '@rsbuild/core': '>=1.0.2'
+ '@tanstack/react-router': ^1.168.21
+ vite: '>=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0'
+ vite-plugin-solid: ^2.11.10 || ^3.0.0-0
+ webpack: '>=5.92.0'
+ peerDependenciesMeta:
+ '@rsbuild/core':
+ optional: true
+ '@tanstack/react-router':
+ optional: true
+ vite:
+ optional: true
+ vite-plugin-solid:
+ optional: true
+ webpack:
+ optional: true
+
+ '@tanstack/router-utils@1.161.6':
+ resolution: {integrity: sha512-nRcYw+w2OEgK6VfjirYvGyPLOK+tZQz1jkYcmH5AjMamQ9PycnlxZF2aEZtPpNoUsaceX2bHptn6Ub5hGXqNvw==}
+ engines: {node: '>=20.19'}
+
+ '@tanstack/store@0.9.3':
+ resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==}
+
+ '@tanstack/virtual-file-routes@1.161.7':
+ resolution: {integrity: sha512-olW33+Cn+bsCsZKPwEGhlkqS6w3M2slFv11JIobdnCFKMLG97oAI2kWKdx5/zsywTL8flpnoIgaZZPlQTFYhdQ==}
+ engines: {node: '>=20.19'}
+ hasBin: true
+
+ '@tybys/wasm-util@0.10.1':
+ resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.28.0':
+ resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+
+ '@types/node@24.12.2':
+ resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==}
+
+ '@types/react-dom@19.2.3':
+ resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
+ peerDependencies:
+ '@types/react': ^19.2.0
+
+ '@types/react@19.2.14':
+ resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
+
+ '@vitejs/plugin-react@6.0.1':
+ resolution: {integrity: sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0
+ babel-plugin-react-compiler: ^1.0.0
+ vite: ^8.0.0
+ peerDependenciesMeta:
+ '@rolldown/plugin-babel':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+
+ acorn@8.16.0:
+ resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ ajv@8.18.0:
+ resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
+
+ ansi-escapes@4.3.2:
+ resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+ engines: {node: '>=8'}
+
+ ansi-escapes@7.3.0:
+ resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==}
+ engines: {node: '>=18'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.2.2:
+ resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
+ engines: {node: '>=12'}
+
+ ansi-styles@3.2.1:
+ resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+ engines: {node: '>=4'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@6.2.3:
+ resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+ engines: {node: '>=12'}
+
+ ansis@4.2.0:
+ resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==}
+ engines: {node: '>=14'}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ array-ify@1.0.0:
+ resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==}
+
+ at-least-node@1.0.0:
+ resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
+ engines: {node: '>= 4.0.0'}
+
+ babel-dead-code-elimination@1.0.12:
+ resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==}
+
+ babel-plugin-react-compiler@1.0.0:
+ resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+ baseline-browser-mapping@2.10.19:
+ resolution: {integrity: sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ bl@4.1.0:
+ resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+
+ brace-expansion@1.1.14:
+ resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.28.2:
+ resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ buffer@5.7.1:
+ resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+
+ cachedir@2.3.0:
+ resolution: {integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==}
+ engines: {node: '>=6'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ caniuse-lite@1.0.30001788:
+ resolution: {integrity: sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==}
+
+ chalk@2.4.2:
+ resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+ engines: {node: '>=4'}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ chardet@0.7.0:
+ resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ cli-cursor@3.1.0:
+ resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
+ engines: {node: '>=8'}
+
+ cli-cursor@5.0.0:
+ resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
+ engines: {node: '>=18'}
+
+ cli-spinners@2.9.2:
+ resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
+ engines: {node: '>=6'}
+
+ cli-truncate@5.2.0:
+ resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==}
+ engines: {node: '>=20'}
+
+ cli-width@3.0.0:
+ resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
+ engines: {node: '>= 10'}
+
+ cliui@8.0.1:
+ resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+ engines: {node: '>=12'}
+
+ clone@1.0.4:
+ resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
+ engines: {node: '>=0.8'}
+
+ color-convert@1.9.3:
+ resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.3:
+ resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ colorette@2.0.20:
+ resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
+
+ commander@14.0.3:
+ resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==}
+ engines: {node: '>=20'}
+
+ commitizen@4.3.1:
+ resolution: {integrity: sha512-gwAPAVTy/j5YcOOebcCRIijn+mSjWJC+IYKivTu6aG8Ei/scoXgfsMRnuAk6b0GRste2J4NGxVdMN3ZpfNaVaw==}
+ engines: {node: '>= 12'}
+ hasBin: true
+
+ compare-func@2.0.0:
+ resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ conventional-changelog-angular@8.3.1:
+ resolution: {integrity: sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==}
+ engines: {node: '>=18'}
+
+ conventional-changelog-conventionalcommits@9.3.1:
+ resolution: {integrity: sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw==}
+ engines: {node: '>=18'}
+
+ conventional-commit-types@3.0.0:
+ resolution: {integrity: sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==}
+
+ conventional-commits-parser@6.4.0:
+ resolution: {integrity: sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ cookie-es@3.1.1:
+ resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==}
+
+ cosmiconfig-typescript-loader@6.3.0:
+ resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==}
+ engines: {node: '>=v18'}
+ peerDependencies:
+ '@types/node': '*'
+ cosmiconfig: '>=9'
+ typescript: '>=5'
+
+ cosmiconfig@9.0.1:
+ resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ typescript: '>=4.9.5'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ cz-conventional-changelog@3.3.0:
+ resolution: {integrity: sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==}
+ engines: {node: '>= 10'}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ dedent@0.7.0:
+ resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
+
+ defaults@1.0.4:
+ resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
+
+ detect-file@1.0.0:
+ resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==}
+ engines: {node: '>=0.10.0'}
+
+ detect-indent@6.1.0:
+ resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
+ engines: {node: '>=8'}
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ diff@8.0.4:
+ resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==}
+ engines: {node: '>=0.3.1'}
+
+ dot-prop@5.3.0:
+ resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==}
+ engines: {node: '>=8'}
+
+ electron-to-chromium@1.5.338:
+ resolution: {integrity: sha512-KVQQ3xko9/coDX3qXLUEEbqkKT8L+1DyAovrtu0Khtrt9wjSZ+7CZV4GVzxFy9Oe1NbrIU1oVXCwHJruIA1PNg==}
+
+ emoji-regex@10.6.0:
+ resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ enhanced-resolve@5.20.1:
+ resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==}
+ engines: {node: '>=10.13.0'}
+
+ env-paths@2.2.1:
+ resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
+ engines: {node: '>=6'}
+
+ environment@1.1.0:
+ resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
+ engines: {node: '>=18'}
+
+ error-ex@1.3.4:
+ resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
+
+ esbuild@0.27.7:
+ resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+
+ eventemitter3@5.0.4:
+ resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
+
+ expand-tilde@2.0.2:
+ resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==}
+ engines: {node: '>=0.10.0'}
+
+ external-editor@3.1.0:
+ resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
+ engines: {node: '>=4'}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-uri@3.1.0:
+ resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ figures@3.2.0:
+ resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
+ engines: {node: '>=8'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-node-modules@2.1.3:
+ resolution: {integrity: sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==}
+
+ find-root@1.1.0:
+ resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
+
+ findup-sync@4.0.0:
+ resolution: {integrity: sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==}
+ engines: {node: '>= 8'}
+
+ fs-extra@9.1.0:
+ resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
+ engines: {node: '>=10'}
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
+ get-east-asian-width@1.5.0:
+ resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==}
+ engines: {node: '>=18'}
+
+ get-tsconfig@4.14.0:
+ resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==}
+
+ git-raw-commits@5.0.1:
+ resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ global-directory@4.0.1:
+ resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==}
+ engines: {node: '>=18'}
+
+ global-modules@1.0.0:
+ resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==}
+ engines: {node: '>=0.10.0'}
+
+ global-prefix@1.0.2:
+ resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==}
+ engines: {node: '>=0.10.0'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ has-flag@3.0.0:
+ resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+ engines: {node: '>=4'}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ homedir-polyfill@1.0.3:
+ resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==}
+ engines: {node: '>=0.10.0'}
+
+ html-parse-stringify@3.0.1:
+ resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
+
+ husky@9.1.7:
+ resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ i18next@26.0.5:
+ resolution: {integrity: sha512-9uHb4T27TdV36phJXcbpnRPt5yzAfqHXVrdASvmHZyPuZJtrLythd+GyXhiaHV5LlpuuskbAqhwPjmfTbKbi8w==}
+ peerDependencies:
+ typescript: ^5 || ^6
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ iconv-lite@0.4.24:
+ resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+ engines: {node: '>=0.10.0'}
+
+ ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
+ import-meta-resolve@4.2.0:
+ resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
+
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+ ini@4.1.1:
+ resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+ inquirer@8.2.5:
+ resolution: {integrity: sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==}
+ engines: {node: '>=12.0.0'}
+
+ is-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+ is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-fullwidth-code-point@5.1.0:
+ resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==}
+ engines: {node: '>=18'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-interactive@1.0.0:
+ resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
+ engines: {node: '>=8'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-obj@2.0.0:
+ resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
+ engines: {node: '>=8'}
+
+ is-plain-obj@4.1.0:
+ resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+ engines: {node: '>=12'}
+
+ is-unicode-supported@0.1.0:
+ resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
+ engines: {node: '>=10'}
+
+ is-utf8@0.2.1:
+ resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==}
+
+ is-windows@1.0.2:
+ resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
+ engines: {node: '>=0.10.0'}
+
+ isbot@5.1.39:
+ resolution: {integrity: sha512-obH0yYahGXdzNxo+djmHhBYThUKDkz565cxkIlt2L9hXfv1NlaLKoDBHo6KxXsYrIXx2RK3x5vY36CfZcobxEw==}
+ engines: {node: '>=18'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ jiti@2.6.1:
+ resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
+ hasBin: true
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@4.1.1:
+ resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
+ hasBin: true
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+
+ json-schema-traverse@1.0.0:
+ resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsonfile@6.2.0:
+ resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
+
+ ky@2.0.1:
+ resolution: {integrity: sha512-HJPEjEpQPZQ5M3G5eu90/LWZDwysCnvqcfbLvq9FUvfizBZRi58WEixswyyI32LOLcFQd43w7kcfgkCPFxDt/Q==}
+ engines: {node: '>=22'}
+
+ lightningcss-android-arm64@1.32.0:
+ resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.32.0:
+ resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.32.0:
+ resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.32.0:
+ resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-musl@1.32.0:
+ resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.32.0:
+ resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
+ engines: {node: '>= 12.0.0'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ lint-staged@16.4.0:
+ resolution: {integrity: sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==}
+ engines: {node: '>=20.17'}
+ hasBin: true
+
+ listr2@9.0.5:
+ resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==}
+ engines: {node: '>=20.0.0'}
+
+ lodash.camelcase@4.3.0:
+ resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+
+ lodash.kebabcase@4.1.1:
+ resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
+
+ lodash.map@4.6.0:
+ resolution: {integrity: sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==}
+
+ lodash.mergewith@4.6.2:
+ resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
+
+ lodash.snakecase@4.1.1:
+ resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
+
+ lodash.startcase@4.4.0:
+ resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==}
+
+ lodash.upperfirst@4.3.1:
+ resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==}
+
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+ log-symbols@4.1.0:
+ resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
+ engines: {node: '>=10'}
+
+ log-update@6.1.0:
+ resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
+ engines: {node: '>=18'}
+
+ longest@2.0.1:
+ resolution: {integrity: sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==}
+ engines: {node: '>=0.10.0'}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ meow@13.2.0:
+ resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==}
+ engines: {node: '>=18'}
+
+ merge@2.1.1:
+ resolution: {integrity: sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mimic-fn@2.1.0:
+ resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+ engines: {node: '>=6'}
+
+ mimic-function@5.0.1:
+ resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
+ engines: {node: '>=18'}
+
+ minimatch@3.1.5:
+ resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
+
+ minimist@1.2.7:
+ resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ mute-stream@0.0.8:
+ resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ node-releases@2.0.37:
+ resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ onetime@5.1.2:
+ resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+ engines: {node: '>=6'}
+
+ onetime@7.0.0:
+ resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
+ engines: {node: '>=18'}
+
+ ora@5.4.1:
+ resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
+ engines: {node: '>=10'}
+
+ os-tmpdir@1.0.2:
+ resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
+ engines: {node: '>=0.10.0'}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse-json@5.2.0:
+ resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+ engines: {node: '>=8'}
+
+ parse-passwd@1.0.0:
+ resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==}
+ engines: {node: '>=0.10.0'}
+
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.2:
+ resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.4:
+ resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
+ engines: {node: '>=12'}
+
+ postcss@8.5.10:
+ resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prettier@3.8.3:
+ resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ react-dom@19.2.5:
+ resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==}
+ peerDependencies:
+ react: ^19.2.5
+
+ react-i18next@17.0.3:
+ resolution: {integrity: sha512-x4xjvUNZ56T+zfXWNedNnCET9Xq1IBYWX7IsWo5cCQ/RT+Rm7GWqt0h9PShFi4IhyMnsdiu1C6Jc4DE+/S3PFQ==}
+ peerDependencies:
+ i18next: '>= 26.0.1'
+ react: '>= 16.8.0'
+ react-dom: '*'
+ react-native: '*'
+ typescript: ^5 || ^6
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ typescript:
+ optional: true
+
+ react@19.2.5:
+ resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==}
+ engines: {node: '>=0.10.0'}
+
+ readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ resolve-dir@1.0.1:
+ resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==}
+ engines: {node: '>=0.10.0'}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-from@5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ restore-cursor@3.1.0:
+ resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
+ engines: {node: '>=8'}
+
+ restore-cursor@5.1.0:
+ resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
+ engines: {node: '>=18'}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ rolldown@1.0.0-rc.15:
+ resolution: {integrity: sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
+ run-async@2.4.1:
+ resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
+ engines: {node: '>=0.12.0'}
+
+ rxjs@7.8.2:
+ resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.7.4:
+ resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ seroval-plugins@1.5.2:
+ resolution: {integrity: sha512-qpY0Cl+fKYFn4GOf3cMiq6l72CpuVaawb6ILjubOQ+diJ54LfOWaSSPsaswN8DRPIPW4Yq+tE1k5aKd7ILyaFg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ seroval: ^1.0
+
+ seroval@1.5.2:
+ resolution: {integrity: sha512-xcRN39BdsnO9Tf+VzsE7b3JyTJASItIV1FVFewJKCFcW4s4haIKS3e6vj8PGB9qBwC7tnuOywQMdv5N4qkzi7Q==}
+ engines: {node: '>=10'}
+
+ signal-exit@3.0.7:
+ resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ slice-ansi@7.1.2:
+ resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==}
+ engines: {node: '>=18'}
+
+ slice-ansi@8.0.0:
+ resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==}
+ engines: {node: '>=20'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ string-argv@0.3.2:
+ resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
+ engines: {node: '>=0.6.19'}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@7.2.0:
+ resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
+ engines: {node: '>=18'}
+
+ string-width@8.2.0:
+ resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==}
+ engines: {node: '>=20'}
+
+ string_decoder@1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.2.0:
+ resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
+ engines: {node: '>=12'}
+
+ strip-bom@4.0.0:
+ resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
+ engines: {node: '>=8'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ supports-color@5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ tailwindcss@4.2.2:
+ resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==}
+
+ tapable@2.3.2:
+ resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==}
+ engines: {node: '>=6'}
+
+ through@2.3.8:
+ resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+
+ tinyexec@1.1.1:
+ resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==}
+ engines: {node: '>=18'}
+
+ tinyglobby@0.2.16:
+ resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==}
+ engines: {node: '>=12.0.0'}
+
+ tmp@0.0.33:
+ resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
+ engines: {node: '>=0.6.0'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ tsx@4.21.0:
+ resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
+ type-fest@0.21.3:
+ resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+ engines: {node: '>=10'}
+
+ typescript@6.0.2:
+ resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@7.16.0:
+ resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
+
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
+ unplugin@2.3.11:
+ resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==}
+ engines: {node: '>=18.12.0'}
+
+ update-browserslist-db@1.2.3:
+ resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ use-sync-external-store@1.6.0:
+ resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ vite@8.0.8:
+ resolution: {integrity: sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ '@vitejs/devtools': ^0.1.0
+ 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
+ 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
+
+ void-elements@3.1.0:
+ resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
+ engines: {node: '>=0.10.0'}
+
+ wcwidth@1.0.1:
+ resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
+
+ webpack-virtual-modules@0.6.2:
+ resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
+
+ which@1.3.1:
+ resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@9.0.2:
+ resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
+ engines: {node: '>=18'}
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yaml@2.8.3:
+ resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
+ yargs-parser@21.1.1:
+ resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+ engines: {node: '>=12'}
+
+ yargs@17.7.2:
+ resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+ engines: {node: '>=12'}
+
+ zod@3.25.76:
+ resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
+
+ zustand@5.0.12:
+ resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': '>=18.0.0'
+ immer: '>=9.0.6'
+ react: '>=18.0.0'
+ use-sync-external-store: '>=1.2.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ use-sync-external-store:
+ optional: true
+
+snapshots:
+
+ '@babel/code-frame@7.29.0':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.29.0': {}
+
+ '@babel/core@7.29.0':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
+ '@babel/helpers': 7.29.2
+ '@babel/parser': 7.29.2
+ '@babel/template': 7.28.6
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.29.1':
+ dependencies:
+ '@babel/parser': 7.29.2
+ '@babel/types': 7.29.0
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.28.6':
+ dependencies:
+ '@babel/compat-data': 7.29.0
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.28.2
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-module-imports@7.28.6':
+ dependencies:
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.29.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.28.6': {}
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.28.5': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helpers@7.29.2':
+ dependencies:
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
+
+ '@babel/parser@7.29.2':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/runtime@7.29.2': {}
+
+ '@babel/template@7.28.6':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/parser': 7.29.2
+ '@babel/types': 7.29.0
+
+ '@babel/traverse@7.29.0':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.29.2
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.29.0':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@biomejs/biome@2.4.12':
+ optionalDependencies:
+ '@biomejs/cli-darwin-arm64': 2.4.12
+ '@biomejs/cli-darwin-x64': 2.4.12
+ '@biomejs/cli-linux-arm64': 2.4.12
+ '@biomejs/cli-linux-arm64-musl': 2.4.12
+ '@biomejs/cli-linux-x64': 2.4.12
+ '@biomejs/cli-linux-x64-musl': 2.4.12
+ '@biomejs/cli-win32-arm64': 2.4.12
+ '@biomejs/cli-win32-x64': 2.4.12
+
+ '@biomejs/cli-darwin-arm64@2.4.12':
+ optional: true
+
+ '@biomejs/cli-darwin-x64@2.4.12':
+ optional: true
+
+ '@biomejs/cli-linux-arm64-musl@2.4.12':
+ optional: true
+
+ '@biomejs/cli-linux-arm64@2.4.12':
+ optional: true
+
+ '@biomejs/cli-linux-x64-musl@2.4.12':
+ optional: true
+
+ '@biomejs/cli-linux-x64@2.4.12':
+ optional: true
+
+ '@biomejs/cli-win32-arm64@2.4.12':
+ optional: true
+
+ '@biomejs/cli-win32-x64@2.4.12':
+ optional: true
+
+ '@commitlint/cli@20.5.0(@types/node@24.12.2)(conventional-commits-parser@6.4.0)(typescript@6.0.2)':
+ dependencies:
+ '@commitlint/format': 20.5.0
+ '@commitlint/lint': 20.5.0
+ '@commitlint/load': 20.5.0(@types/node@24.12.2)(typescript@6.0.2)
+ '@commitlint/read': 20.5.0(conventional-commits-parser@6.4.0)
+ '@commitlint/types': 20.5.0
+ tinyexec: 1.1.1
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - '@types/node'
+ - conventional-commits-filter
+ - conventional-commits-parser
+ - typescript
+
+ '@commitlint/config-conventional@20.5.0':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ conventional-changelog-conventionalcommits: 9.3.1
+
+ '@commitlint/config-validator@20.5.0':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ ajv: 8.18.0
+
+ '@commitlint/ensure@20.5.0':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ lodash.camelcase: 4.3.0
+ lodash.kebabcase: 4.1.1
+ lodash.snakecase: 4.1.1
+ lodash.startcase: 4.4.0
+ lodash.upperfirst: 4.3.1
+
+ '@commitlint/execute-rule@20.0.0': {}
+
+ '@commitlint/format@20.5.0':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ picocolors: 1.1.1
+
+ '@commitlint/is-ignored@20.5.0':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ semver: 7.7.4
+
+ '@commitlint/lint@20.5.0':
+ dependencies:
+ '@commitlint/is-ignored': 20.5.0
+ '@commitlint/parse': 20.5.0
+ '@commitlint/rules': 20.5.0
+ '@commitlint/types': 20.5.0
+
+ '@commitlint/load@20.5.0(@types/node@24.12.2)(typescript@6.0.2)':
+ dependencies:
+ '@commitlint/config-validator': 20.5.0
+ '@commitlint/execute-rule': 20.0.0
+ '@commitlint/resolve-extends': 20.5.0
+ '@commitlint/types': 20.5.0
+ cosmiconfig: 9.0.1(typescript@6.0.2)
+ cosmiconfig-typescript-loader: 6.3.0(@types/node@24.12.2)(cosmiconfig@9.0.1(typescript@6.0.2))(typescript@6.0.2)
+ is-plain-obj: 4.1.0
+ lodash.mergewith: 4.6.2
+ picocolors: 1.1.1
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+
+ '@commitlint/message@20.4.3': {}
+
+ '@commitlint/parse@20.5.0':
+ dependencies:
+ '@commitlint/types': 20.5.0
+ conventional-changelog-angular: 8.3.1
+ conventional-commits-parser: 6.4.0
+
+ '@commitlint/read@20.5.0(conventional-commits-parser@6.4.0)':
+ dependencies:
+ '@commitlint/top-level': 20.4.3
+ '@commitlint/types': 20.5.0
+ git-raw-commits: 5.0.1(conventional-commits-parser@6.4.0)
+ minimist: 1.2.8
+ tinyexec: 1.1.1
+ transitivePeerDependencies:
+ - conventional-commits-filter
+ - conventional-commits-parser
+
+ '@commitlint/resolve-extends@20.5.0':
+ dependencies:
+ '@commitlint/config-validator': 20.5.0
+ '@commitlint/types': 20.5.0
+ global-directory: 4.0.1
+ import-meta-resolve: 4.2.0
+ lodash.mergewith: 4.6.2
+ resolve-from: 5.0.0
+
+ '@commitlint/rules@20.5.0':
+ dependencies:
+ '@commitlint/ensure': 20.5.0
+ '@commitlint/message': 20.4.3
+ '@commitlint/to-lines': 20.0.0
+ '@commitlint/types': 20.5.0
+
+ '@commitlint/to-lines@20.0.0': {}
+
+ '@commitlint/top-level@20.4.3':
+ dependencies:
+ escalade: 3.2.0
+
+ '@commitlint/types@20.5.0':
+ dependencies:
+ conventional-commits-parser: 6.4.0
+ picocolors: 1.1.1
+
+ '@conventional-changelog/git-client@2.7.0(conventional-commits-parser@6.4.0)':
+ dependencies:
+ '@simple-libs/child-process-utils': 1.0.2
+ '@simple-libs/stream-utils': 1.2.0
+ semver: 7.7.4
+ optionalDependencies:
+ conventional-commits-parser: 6.4.0
+
+ '@emnapi/core@1.9.2':
+ dependencies:
+ '@emnapi/wasi-threads': 1.2.1
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.9.2':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.2.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@esbuild/aix-ppc64@0.27.7':
+ optional: true
+
+ '@esbuild/android-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/android-arm@0.27.7':
+ optional: true
+
+ '@esbuild/android-x64@0.27.7':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/darwin-x64@0.27.7':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-arm@0.27.7':
+ optional: true
+
+ '@esbuild/linux-ia32@0.27.7':
+ optional: true
+
+ '@esbuild/linux-loong64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.27.7':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-s390x@0.27.7':
+ optional: true
+
+ '@esbuild/linux-x64@0.27.7':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/sunos-x64@0.27.7':
+ optional: true
+
+ '@esbuild/win32-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/win32-ia32@0.27.7':
+ optional: true
+
+ '@esbuild/win32-x64@0.27.7':
+ optional: true
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)':
+ dependencies:
+ '@emnapi/core': 1.9.2
+ '@emnapi/runtime': 1.9.2
+ '@tybys/wasm-util': 0.10.1
+ optional: true
+
+ '@oxc-project/types@0.124.0': {}
+
+ '@rolldown/binding-android-arm64@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-darwin-arm64@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-darwin-x64@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-freebsd-x64@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-musl@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-linux-x64-gnu@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-linux-x64-musl@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-openharmony-arm64@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-wasm32-wasi@1.0.0-rc.15':
+ dependencies:
+ '@emnapi/core': 1.9.2
+ '@emnapi/runtime': 1.9.2
+ '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)
+ optional: true
+
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/binding-win32-x64-msvc@1.0.0-rc.15':
+ optional: true
+
+ '@rolldown/plugin-babel@0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.0-rc.15)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))':
+ dependencies:
+ '@babel/core': 7.29.0
+ picomatch: 4.0.4
+ rolldown: 1.0.0-rc.15
+ optionalDependencies:
+ '@babel/runtime': 7.29.2
+ vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)
+
+ '@rolldown/pluginutils@1.0.0-rc.15': {}
+
+ '@rolldown/pluginutils@1.0.0-rc.7': {}
+
+ '@simple-libs/child-process-utils@1.0.2':
+ dependencies:
+ '@simple-libs/stream-utils': 1.2.0
+
+ '@simple-libs/stream-utils@1.2.0': {}
+
+ '@tailwindcss/node@4.2.2':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.20.1
+ jiti: 2.6.1
+ lightningcss: 1.32.0
+ magic-string: 0.30.21
+ source-map-js: 1.2.1
+ tailwindcss: 4.2.2
+
+ '@tailwindcss/oxide-android-arm64@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide@4.2.2':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.2.2
+ '@tailwindcss/oxide-darwin-arm64': 4.2.2
+ '@tailwindcss/oxide-darwin-x64': 4.2.2
+ '@tailwindcss/oxide-freebsd-x64': 4.2.2
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.2
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.2.2
+ '@tailwindcss/oxide-linux-arm64-musl': 4.2.2
+ '@tailwindcss/oxide-linux-x64-gnu': 4.2.2
+ '@tailwindcss/oxide-linux-x64-musl': 4.2.2
+ '@tailwindcss/oxide-wasm32-wasi': 4.2.2
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2
+ '@tailwindcss/oxide-win32-x64-msvc': 4.2.2
+
+ '@tailwindcss/vite@4.2.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))':
+ dependencies:
+ '@tailwindcss/node': 4.2.2
+ '@tailwindcss/oxide': 4.2.2
+ tailwindcss: 4.2.2
+ vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)
+
+ '@tanstack/history@1.161.6': {}
+
+ '@tanstack/query-core@5.99.0': {}
+
+ '@tanstack/query-devtools@5.99.0': {}
+
+ '@tanstack/react-query-devtools@5.99.0(@tanstack/react-query@5.99.0(react@19.2.5))(react@19.2.5)':
+ dependencies:
+ '@tanstack/query-devtools': 5.99.0
+ '@tanstack/react-query': 5.99.0(react@19.2.5)
+ react: 19.2.5
+
+ '@tanstack/react-query@5.99.0(react@19.2.5)':
+ dependencies:
+ '@tanstack/query-core': 5.99.0
+ react: 19.2.5
+
+ '@tanstack/react-router@1.168.22(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
+ dependencies:
+ '@tanstack/history': 1.161.6
+ '@tanstack/react-store': 0.9.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
+ '@tanstack/router-core': 1.168.15
+ isbot: 5.1.39
+ react: 19.2.5
+ react-dom: 19.2.5(react@19.2.5)
+
+ '@tanstack/react-store@0.9.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
+ dependencies:
+ '@tanstack/store': 0.9.3
+ react: 19.2.5
+ react-dom: 19.2.5(react@19.2.5)
+ use-sync-external-store: 1.6.0(react@19.2.5)
+
+ '@tanstack/router-cli@1.166.33':
+ dependencies:
+ '@tanstack/router-generator': 1.166.32
+ chokidar: 3.6.0
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@tanstack/router-core@1.168.15':
+ dependencies:
+ '@tanstack/history': 1.161.6
+ cookie-es: 3.1.1
+ seroval: 1.5.2
+ seroval-plugins: 1.5.2(seroval@1.5.2)
+
+ '@tanstack/router-generator@1.166.32':
+ dependencies:
+ '@babel/types': 7.29.0
+ '@tanstack/router-core': 1.168.15
+ '@tanstack/router-utils': 1.161.6
+ '@tanstack/virtual-file-routes': 1.161.7
+ magic-string: 0.30.21
+ prettier: 3.8.3
+ tsx: 4.21.0
+ zod: 3.25.76
+ transitivePeerDependencies:
+ - supports-color
+
+ '@tanstack/router-plugin@1.167.22(@tanstack/react-router@1.168.22(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0)
+ '@babel/template': 7.28.6
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
+ '@tanstack/router-core': 1.168.15
+ '@tanstack/router-generator': 1.166.32
+ '@tanstack/router-utils': 1.161.6
+ '@tanstack/virtual-file-routes': 1.161.7
+ chokidar: 3.6.0
+ unplugin: 2.3.11
+ zod: 3.25.76
+ optionalDependencies:
+ '@tanstack/react-router': 1.168.22(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
+ vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@tanstack/router-utils@1.161.6':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/parser': 7.29.2
+ '@babel/types': 7.29.0
+ ansis: 4.2.0
+ babel-dead-code-elimination: 1.0.12
+ diff: 8.0.4
+ pathe: 2.0.3
+ tinyglobby: 0.2.16
+ transitivePeerDependencies:
+ - supports-color
+
+ '@tanstack/store@0.9.3': {}
+
+ '@tanstack/virtual-file-routes@1.161.7': {}
+
+ '@tybys/wasm-util@0.10.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.29.2
+ '@babel/types': 7.29.0
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.28.0
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.29.2
+ '@babel/types': 7.29.0
+
+ '@types/babel__traverse@7.28.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@types/node@24.12.2':
+ dependencies:
+ undici-types: 7.16.0
+
+ '@types/react-dom@19.2.3(@types/react@19.2.14)':
+ dependencies:
+ '@types/react': 19.2.14
+
+ '@types/react@19.2.14':
+ dependencies:
+ csstype: 3.2.3
+
+ '@vitejs/plugin-react@6.0.1(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.0-rc.15)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)))(babel-plugin-react-compiler@1.0.0)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))':
+ dependencies:
+ '@rolldown/pluginutils': 1.0.0-rc.7
+ vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)
+ optionalDependencies:
+ '@rolldown/plugin-babel': 0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.0-rc.15)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))
+ babel-plugin-react-compiler: 1.0.0
+
+ acorn@8.16.0: {}
+
+ ajv@8.18.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-uri: 3.1.0
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+
+ ansi-escapes@4.3.2:
+ dependencies:
+ type-fest: 0.21.3
+
+ ansi-escapes@7.3.0:
+ dependencies:
+ environment: 1.1.0
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.2.2: {}
+
+ ansi-styles@3.2.1:
+ dependencies:
+ color-convert: 1.9.3
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@6.2.3: {}
+
+ ansis@4.2.0: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.2
+
+ argparse@2.0.1: {}
+
+ array-ify@1.0.0: {}
+
+ at-least-node@1.0.0: {}
+
+ babel-dead-code-elimination@1.0.12:
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/parser': 7.29.2
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-react-compiler@1.0.0:
+ dependencies:
+ '@babel/types': 7.29.0
+
+ balanced-match@1.0.2: {}
+
+ base64-js@1.5.1: {}
+
+ baseline-browser-mapping@2.10.19: {}
+
+ binary-extensions@2.3.0: {}
+
+ bl@4.1.0:
+ dependencies:
+ buffer: 5.7.1
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+
+ brace-expansion@1.1.14:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.28.2:
+ dependencies:
+ baseline-browser-mapping: 2.10.19
+ caniuse-lite: 1.0.30001788
+ electron-to-chromium: 1.5.338
+ node-releases: 2.0.37
+ update-browserslist-db: 1.2.3(browserslist@4.28.2)
+
+ buffer@5.7.1:
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+
+ cachedir@2.3.0: {}
+
+ callsites@3.1.0: {}
+
+ caniuse-lite@1.0.30001788: {}
+
+ chalk@2.4.2:
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 5.5.0
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ chardet@0.7.0: {}
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ cli-cursor@3.1.0:
+ dependencies:
+ restore-cursor: 3.1.0
+
+ cli-cursor@5.0.0:
+ dependencies:
+ restore-cursor: 5.1.0
+
+ cli-spinners@2.9.2: {}
+
+ cli-truncate@5.2.0:
+ dependencies:
+ slice-ansi: 8.0.0
+ string-width: 8.2.0
+
+ cli-width@3.0.0: {}
+
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
+ clone@1.0.4: {}
+
+ color-convert@1.9.3:
+ dependencies:
+ color-name: 1.1.3
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.3: {}
+
+ color-name@1.1.4: {}
+
+ colorette@2.0.20: {}
+
+ commander@14.0.3: {}
+
+ commitizen@4.3.1(@types/node@24.12.2)(typescript@6.0.2):
+ dependencies:
+ cachedir: 2.3.0
+ cz-conventional-changelog: 3.3.0(@types/node@24.12.2)(typescript@6.0.2)
+ dedent: 0.7.0
+ detect-indent: 6.1.0
+ find-node-modules: 2.1.3
+ find-root: 1.1.0
+ fs-extra: 9.1.0
+ glob: 7.2.3
+ inquirer: 8.2.5
+ is-utf8: 0.2.1
+ lodash: 4.17.21
+ minimist: 1.2.7
+ strip-bom: 4.0.0
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+
+ compare-func@2.0.0:
+ dependencies:
+ array-ify: 1.0.0
+ dot-prop: 5.3.0
+
+ concat-map@0.0.1: {}
+
+ conventional-changelog-angular@8.3.1:
+ dependencies:
+ compare-func: 2.0.0
+
+ conventional-changelog-conventionalcommits@9.3.1:
+ dependencies:
+ compare-func: 2.0.0
+
+ conventional-commit-types@3.0.0: {}
+
+ conventional-commits-parser@6.4.0:
+ dependencies:
+ '@simple-libs/stream-utils': 1.2.0
+ meow: 13.2.0
+
+ convert-source-map@2.0.0: {}
+
+ cookie-es@3.1.1: {}
+
+ cosmiconfig-typescript-loader@6.3.0(@types/node@24.12.2)(cosmiconfig@9.0.1(typescript@6.0.2))(typescript@6.0.2):
+ dependencies:
+ '@types/node': 24.12.2
+ cosmiconfig: 9.0.1(typescript@6.0.2)
+ jiti: 2.6.1
+ typescript: 6.0.2
+
+ cosmiconfig@9.0.1(typescript@6.0.2):
+ dependencies:
+ env-paths: 2.2.1
+ import-fresh: 3.3.1
+ js-yaml: 4.1.1
+ parse-json: 5.2.0
+ optionalDependencies:
+ typescript: 6.0.2
+
+ csstype@3.2.3: {}
+
+ cz-conventional-changelog@3.3.0(@types/node@24.12.2)(typescript@6.0.2):
+ dependencies:
+ chalk: 2.4.2
+ commitizen: 4.3.1(@types/node@24.12.2)(typescript@6.0.2)
+ conventional-commit-types: 3.0.0
+ lodash.map: 4.6.0
+ longest: 2.0.1
+ word-wrap: 1.2.5
+ optionalDependencies:
+ '@commitlint/load': 20.5.0(@types/node@24.12.2)(typescript@6.0.2)
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ dedent@0.7.0: {}
+
+ defaults@1.0.4:
+ dependencies:
+ clone: 1.0.4
+
+ detect-file@1.0.0: {}
+
+ detect-indent@6.1.0: {}
+
+ detect-libc@2.1.2: {}
+
+ diff@8.0.4: {}
+
+ dot-prop@5.3.0:
+ dependencies:
+ is-obj: 2.0.0
+
+ electron-to-chromium@1.5.338: {}
+
+ emoji-regex@10.6.0: {}
+
+ emoji-regex@8.0.0: {}
+
+ enhanced-resolve@5.20.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.2
+
+ env-paths@2.2.1: {}
+
+ environment@1.1.0: {}
+
+ error-ex@1.3.4:
+ dependencies:
+ is-arrayish: 0.2.1
+
+ esbuild@0.27.7:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.27.7
+ '@esbuild/android-arm': 0.27.7
+ '@esbuild/android-arm64': 0.27.7
+ '@esbuild/android-x64': 0.27.7
+ '@esbuild/darwin-arm64': 0.27.7
+ '@esbuild/darwin-x64': 0.27.7
+ '@esbuild/freebsd-arm64': 0.27.7
+ '@esbuild/freebsd-x64': 0.27.7
+ '@esbuild/linux-arm': 0.27.7
+ '@esbuild/linux-arm64': 0.27.7
+ '@esbuild/linux-ia32': 0.27.7
+ '@esbuild/linux-loong64': 0.27.7
+ '@esbuild/linux-mips64el': 0.27.7
+ '@esbuild/linux-ppc64': 0.27.7
+ '@esbuild/linux-riscv64': 0.27.7
+ '@esbuild/linux-s390x': 0.27.7
+ '@esbuild/linux-x64': 0.27.7
+ '@esbuild/netbsd-arm64': 0.27.7
+ '@esbuild/netbsd-x64': 0.27.7
+ '@esbuild/openbsd-arm64': 0.27.7
+ '@esbuild/openbsd-x64': 0.27.7
+ '@esbuild/openharmony-arm64': 0.27.7
+ '@esbuild/sunos-x64': 0.27.7
+ '@esbuild/win32-arm64': 0.27.7
+ '@esbuild/win32-ia32': 0.27.7
+ '@esbuild/win32-x64': 0.27.7
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@1.0.5: {}
+
+ eventemitter3@5.0.4: {}
+
+ expand-tilde@2.0.2:
+ dependencies:
+ homedir-polyfill: 1.0.3
+
+ external-editor@3.1.0:
+ dependencies:
+ chardet: 0.7.0
+ iconv-lite: 0.4.24
+ tmp: 0.0.33
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-uri@3.1.0: {}
+
+ fdir@6.5.0(picomatch@4.0.4):
+ optionalDependencies:
+ picomatch: 4.0.4
+
+ figures@3.2.0:
+ dependencies:
+ escape-string-regexp: 1.0.5
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-node-modules@2.1.3:
+ dependencies:
+ findup-sync: 4.0.0
+ merge: 2.1.1
+
+ find-root@1.1.0: {}
+
+ findup-sync@4.0.0:
+ dependencies:
+ detect-file: 1.0.0
+ is-glob: 4.0.3
+ micromatch: 4.0.8
+ resolve-dir: 1.0.1
+
+ fs-extra@9.1.0:
+ dependencies:
+ at-least-node: 1.0.0
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
+ fs.realpath@1.0.0: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ gensync@1.0.0-beta.2: {}
+
+ get-caller-file@2.0.5: {}
+
+ get-east-asian-width@1.5.0: {}
+
+ get-tsconfig@4.14.0:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ git-raw-commits@5.0.1(conventional-commits-parser@6.4.0):
+ dependencies:
+ '@conventional-changelog/git-client': 2.7.0(conventional-commits-parser@6.4.0)
+ meow: 13.2.0
+ transitivePeerDependencies:
+ - conventional-commits-filter
+ - conventional-commits-parser
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@7.2.3:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.5
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ global-directory@4.0.1:
+ dependencies:
+ ini: 4.1.1
+
+ global-modules@1.0.0:
+ dependencies:
+ global-prefix: 1.0.2
+ is-windows: 1.0.2
+ resolve-dir: 1.0.1
+
+ global-prefix@1.0.2:
+ dependencies:
+ expand-tilde: 2.0.2
+ homedir-polyfill: 1.0.3
+ ini: 1.3.8
+ is-windows: 1.0.2
+ which: 1.3.1
+
+ graceful-fs@4.2.11: {}
+
+ has-flag@3.0.0: {}
+
+ has-flag@4.0.0: {}
+
+ homedir-polyfill@1.0.3:
+ dependencies:
+ parse-passwd: 1.0.0
+
+ html-parse-stringify@3.0.1:
+ dependencies:
+ void-elements: 3.1.0
+
+ husky@9.1.7: {}
+
+ i18next@26.0.5(typescript@6.0.2):
+ dependencies:
+ '@babel/runtime': 7.29.2
+ optionalDependencies:
+ typescript: 6.0.2
+
+ iconv-lite@0.4.24:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ ieee754@1.2.1: {}
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ import-meta-resolve@4.2.0: {}
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
+ ini@1.3.8: {}
+
+ ini@4.1.1: {}
+
+ inquirer@8.2.5:
+ dependencies:
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ cli-cursor: 3.1.0
+ cli-width: 3.0.0
+ external-editor: 3.1.0
+ figures: 3.2.0
+ lodash: 4.17.21
+ mute-stream: 0.0.8
+ ora: 5.4.1
+ run-async: 2.4.1
+ rxjs: 7.8.2
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ through: 2.3.8
+ wrap-ansi: 7.0.0
+
+ is-arrayish@0.2.1: {}
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-fullwidth-code-point@5.1.0:
+ dependencies:
+ get-east-asian-width: 1.5.0
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-interactive@1.0.0: {}
+
+ is-number@7.0.0: {}
+
+ is-obj@2.0.0: {}
+
+ is-plain-obj@4.1.0: {}
+
+ is-unicode-supported@0.1.0: {}
+
+ is-utf8@0.2.1: {}
+
+ is-windows@1.0.2: {}
+
+ isbot@5.1.39: {}
+
+ isexe@2.0.0: {}
+
+ jiti@2.6.1: {}
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@4.1.1:
+ dependencies:
+ argparse: 2.0.1
+
+ jsesc@3.1.0: {}
+
+ json-parse-even-better-errors@2.3.1: {}
+
+ json-schema-traverse@1.0.0: {}
+
+ json5@2.2.3: {}
+
+ jsonfile@6.2.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ ky@2.0.1: {}
+
+ lightningcss-android-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-x64@1.32.0:
+ optional: true
+
+ lightningcss-freebsd-x64@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.32.0:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ optional: true
+
+ lightningcss@1.32.0:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.32.0
+ lightningcss-darwin-arm64: 1.32.0
+ lightningcss-darwin-x64: 1.32.0
+ lightningcss-freebsd-x64: 1.32.0
+ lightningcss-linux-arm-gnueabihf: 1.32.0
+ lightningcss-linux-arm64-gnu: 1.32.0
+ lightningcss-linux-arm64-musl: 1.32.0
+ lightningcss-linux-x64-gnu: 1.32.0
+ lightningcss-linux-x64-musl: 1.32.0
+ lightningcss-win32-arm64-msvc: 1.32.0
+ lightningcss-win32-x64-msvc: 1.32.0
+
+ lines-and-columns@1.2.4: {}
+
+ lint-staged@16.4.0:
+ dependencies:
+ commander: 14.0.3
+ listr2: 9.0.5
+ picomatch: 4.0.4
+ string-argv: 0.3.2
+ tinyexec: 1.1.1
+ yaml: 2.8.3
+
+ listr2@9.0.5:
+ dependencies:
+ cli-truncate: 5.2.0
+ colorette: 2.0.20
+ eventemitter3: 5.0.4
+ log-update: 6.1.0
+ rfdc: 1.4.1
+ wrap-ansi: 9.0.2
+
+ lodash.camelcase@4.3.0: {}
+
+ lodash.kebabcase@4.1.1: {}
+
+ lodash.map@4.6.0: {}
+
+ lodash.mergewith@4.6.2: {}
+
+ lodash.snakecase@4.1.1: {}
+
+ lodash.startcase@4.4.0: {}
+
+ lodash.upperfirst@4.3.1: {}
+
+ lodash@4.17.21: {}
+
+ log-symbols@4.1.0:
+ dependencies:
+ chalk: 4.1.2
+ is-unicode-supported: 0.1.0
+
+ log-update@6.1.0:
+ dependencies:
+ ansi-escapes: 7.3.0
+ cli-cursor: 5.0.0
+ slice-ansi: 7.1.2
+ strip-ansi: 7.2.0
+ wrap-ansi: 9.0.2
+
+ longest@2.0.1: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ meow@13.2.0: {}
+
+ merge@2.1.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.2
+
+ mimic-fn@2.1.0: {}
+
+ mimic-function@5.0.1: {}
+
+ minimatch@3.1.5:
+ dependencies:
+ brace-expansion: 1.1.14
+
+ minimist@1.2.7: {}
+
+ minimist@1.2.8: {}
+
+ ms@2.1.3: {}
+
+ mute-stream@0.0.8: {}
+
+ nanoid@3.3.11: {}
+
+ node-releases@2.0.37: {}
+
+ normalize-path@3.0.0: {}
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ onetime@5.1.2:
+ dependencies:
+ mimic-fn: 2.1.0
+
+ onetime@7.0.0:
+ dependencies:
+ mimic-function: 5.0.1
+
+ ora@5.4.1:
+ dependencies:
+ bl: 4.1.0
+ chalk: 4.1.2
+ cli-cursor: 3.1.0
+ cli-spinners: 2.9.2
+ is-interactive: 1.0.0
+ is-unicode-supported: 0.1.0
+ log-symbols: 4.1.0
+ strip-ansi: 6.0.1
+ wcwidth: 1.0.1
+
+ os-tmpdir@1.0.2: {}
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse-json@5.2.0:
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ error-ex: 1.3.4
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.2.4
+
+ parse-passwd@1.0.0: {}
+
+ path-is-absolute@1.0.1: {}
+
+ pathe@2.0.3: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.2: {}
+
+ picomatch@4.0.4: {}
+
+ postcss@8.5.10:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prettier@3.8.3: {}
+
+ react-dom@19.2.5(react@19.2.5):
+ dependencies:
+ react: 19.2.5
+ scheduler: 0.27.0
+
+ react-i18next@17.0.3(i18next@26.0.5(typescript@6.0.2))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@6.0.2):
+ dependencies:
+ '@babel/runtime': 7.29.2
+ html-parse-stringify: 3.0.1
+ i18next: 26.0.5(typescript@6.0.2)
+ react: 19.2.5
+ use-sync-external-store: 1.6.0(react@19.2.5)
+ optionalDependencies:
+ react-dom: 19.2.5(react@19.2.5)
+ typescript: 6.0.2
+
+ react@19.2.5: {}
+
+ readable-stream@3.6.2:
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.2
+
+ require-directory@2.1.1: {}
+
+ require-from-string@2.0.2: {}
+
+ resolve-dir@1.0.1:
+ dependencies:
+ expand-tilde: 2.0.2
+ global-modules: 1.0.0
+
+ resolve-from@4.0.0: {}
+
+ resolve-from@5.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ restore-cursor@3.1.0:
+ dependencies:
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+
+ restore-cursor@5.1.0:
+ dependencies:
+ onetime: 7.0.0
+ signal-exit: 4.1.0
+
+ rfdc@1.4.1: {}
+
+ rolldown@1.0.0-rc.15:
+ dependencies:
+ '@oxc-project/types': 0.124.0
+ '@rolldown/pluginutils': 1.0.0-rc.15
+ optionalDependencies:
+ '@rolldown/binding-android-arm64': 1.0.0-rc.15
+ '@rolldown/binding-darwin-arm64': 1.0.0-rc.15
+ '@rolldown/binding-darwin-x64': 1.0.0-rc.15
+ '@rolldown/binding-freebsd-x64': 1.0.0-rc.15
+ '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.15
+ '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.15
+ '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.15
+ '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.15
+ '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.15
+ '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.15
+ '@rolldown/binding-linux-x64-musl': 1.0.0-rc.15
+ '@rolldown/binding-openharmony-arm64': 1.0.0-rc.15
+ '@rolldown/binding-wasm32-wasi': 1.0.0-rc.15
+ '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.15
+ '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.15
+
+ run-async@2.4.1: {}
+
+ rxjs@7.8.2:
+ dependencies:
+ tslib: 2.8.1
+
+ safe-buffer@5.2.1: {}
+
+ safer-buffer@2.1.2: {}
+
+ scheduler@0.27.0: {}
+
+ semver@6.3.1: {}
+
+ semver@7.7.4: {}
+
+ seroval-plugins@1.5.2(seroval@1.5.2):
+ dependencies:
+ seroval: 1.5.2
+
+ seroval@1.5.2: {}
+
+ signal-exit@3.0.7: {}
+
+ signal-exit@4.1.0: {}
+
+ slice-ansi@7.1.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ is-fullwidth-code-point: 5.1.0
+
+ slice-ansi@8.0.0:
+ dependencies:
+ ansi-styles: 6.2.3
+ is-fullwidth-code-point: 5.1.0
+
+ source-map-js@1.2.1: {}
+
+ string-argv@0.3.2: {}
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@7.2.0:
+ dependencies:
+ emoji-regex: 10.6.0
+ get-east-asian-width: 1.5.0
+ strip-ansi: 7.2.0
+
+ string-width@8.2.0:
+ dependencies:
+ get-east-asian-width: 1.5.0
+ strip-ansi: 7.2.0
+
+ string_decoder@1.3.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.2.0:
+ dependencies:
+ ansi-regex: 6.2.2
+
+ strip-bom@4.0.0: {}
+
+ strip-json-comments@3.1.1: {}
+
+ supports-color@5.5.0:
+ dependencies:
+ has-flag: 3.0.0
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ tailwindcss@4.2.2: {}
+
+ tapable@2.3.2: {}
+
+ through@2.3.8: {}
+
+ tinyexec@1.1.1: {}
+
+ tinyglobby@0.2.16:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.4)
+ picomatch: 4.0.4
+
+ tmp@0.0.33:
+ dependencies:
+ os-tmpdir: 1.0.2
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ tslib@2.8.1: {}
+
+ tsx@4.21.0:
+ dependencies:
+ esbuild: 0.27.7
+ get-tsconfig: 4.14.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ type-fest@0.21.3: {}
+
+ typescript@6.0.2: {}
+
+ undici-types@7.16.0: {}
+
+ universalify@2.0.1: {}
+
+ unplugin@2.3.11:
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ acorn: 8.16.0
+ picomatch: 4.0.4
+ webpack-virtual-modules: 0.6.2
+
+ update-browserslist-db@1.2.3(browserslist@4.28.2):
+ dependencies:
+ browserslist: 4.28.2
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ use-sync-external-store@1.6.0(react@19.2.5):
+ dependencies:
+ react: 19.2.5
+
+ util-deprecate@1.0.2: {}
+
+ vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3):
+ dependencies:
+ lightningcss: 1.32.0
+ picomatch: 4.0.4
+ postcss: 8.5.10
+ rolldown: 1.0.0-rc.15
+ tinyglobby: 0.2.16
+ optionalDependencies:
+ '@types/node': 24.12.2
+ esbuild: 0.27.7
+ fsevents: 2.3.3
+ jiti: 2.6.1
+ tsx: 4.21.0
+ yaml: 2.8.3
+
+ void-elements@3.1.0: {}
+
+ wcwidth@1.0.1:
+ dependencies:
+ defaults: 1.0.4
+
+ webpack-virtual-modules@0.6.2: {}
+
+ which@1.3.1:
+ dependencies:
+ isexe: 2.0.0
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@9.0.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 7.2.0
+ strip-ansi: 7.2.0
+
+ wrappy@1.0.2: {}
+
+ y18n@5.0.8: {}
+
+ yallist@3.1.1: {}
+
+ yaml@2.8.3: {}
+
+ yargs-parser@21.1.1: {}
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
+ zod@3.25.76: {}
+
+ zustand@5.0.12(@types/react@19.2.14)(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)):
+ optionalDependencies:
+ '@types/react': 19.2.14
+ react: 19.2.5
+ use-sync-external-store: 1.6.0(react@19.2.5)
diff --git a/public/favicon.svg b/public/favicon.svg
new file mode 100644
index 0000000..6893eb1
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/constants/index.ts b/src/constants/index.ts
new file mode 100644
index 0000000..03dc5aa
--- /dev/null
+++ b/src/constants/index.ts
@@ -0,0 +1,41 @@
+/** @description 应用启动阶段使用的根节点常量。 */
+export const APP_ROOT_ELEMENT_ID = 'root'
+
+/** @description 应用名称,用于文档标题和分享元信息。 */
+export const APP_NAME = 'React SPA Template'
+
+/** @description 应用默认的页面描述,用于 SEO 和分享卡片。 */
+export const APP_DEFAULT_DESCRIPTION =
+ 'A frontend scaffold with Vite, React, TanStack Router, TanStack Query, Zustand, and Biome.'
+
+/** @description 认证状态持久化到浏览器时使用的存储键。 */
+export const AUTH_STORAGE_KEY = 'auth-session'
+
+/** @description 接口请求的默认超时时间,单位为毫秒。 */
+export const DEFAULT_REQUEST_TIMEOUT_MS = 15_000
+
+/** @description 请求默认声明可接收的响应内容类型。 */
+export const DEFAULT_REQUEST_ACCEPT_HEADER = 'application/json'
+
+/** @description 请求层统一使用的错误提示文案。 */
+export const API_ERROR_MESSAGES = {
+ timeout: 'Request timed out',
+ unexpected: 'Unexpected request error',
+} as const
+
+/** @description TanStack Query 默认的缓存新鲜时间。 */
+export const QUERY_DEFAULT_STALE_TIME_MS = 30_000
+
+/** @description TanStack Query 默认的缓存回收时间。 */
+export const QUERY_DEFAULT_GC_TIME_MS = 5 * 60_000
+
+/** @description 查询请求默认允许的最大重试次数。 */
+export const QUERY_RETRY_LIMIT = 2
+
+/** @description 可被视为瞬时失败并允许重试的状态码集合。 */
+export const QUERY_RETRYABLE_STATUS_CODES = [
+ 408, 429, 500, 502, 503, 504,
+] as const
+
+/** @description 国际化语言设置持久化到浏览器时使用的存储键。 */
+export const I18N_LANGUAGE_STORAGE_KEY = 'app-language'
diff --git a/src/i18n/index.ts b/src/i18n/index.ts
new file mode 100644
index 0000000..61e0cb6
--- /dev/null
+++ b/src/i18n/index.ts
@@ -0,0 +1,112 @@
+import i18n from 'i18next'
+import { initReactI18next } from 'react-i18next'
+
+import { I18N_LANGUAGE_STORAGE_KEY } from '@/constants'
+import enUSCommon from '@/locales/en-US/common'
+import zhCNCommon from '@/locales/zh-CN/common'
+
+export const supportedLanguages = ['zh-CN', 'en-US'] as const
+export type AppLanguage = (typeof supportedLanguages)[number]
+
+const defaultLanguage: AppLanguage = 'zh-CN'
+
+/** @description 判断给定语言是否在当前应用支持列表中。 */
+export function isSupportedLanguage(
+ value: string | null | undefined,
+): value is AppLanguage {
+ return supportedLanguages.includes(value as AppLanguage)
+}
+
+/** @description 从浏览器设置中推断最匹配的语言。 */
+function detectBrowserLanguage() {
+ if (typeof navigator === 'undefined') {
+ return defaultLanguage
+ }
+
+ const browserLanguages = [...navigator.languages, navigator.language]
+
+ for (const language of browserLanguages) {
+ if (isSupportedLanguage(language)) {
+ return language
+ }
+
+ const normalizedLanguage = language.toLowerCase()
+
+ if (normalizedLanguage.startsWith('zh')) {
+ return 'zh-CN'
+ }
+
+ if (normalizedLanguage.startsWith('en')) {
+ return 'en-US'
+ }
+ }
+
+ return defaultLanguage
+}
+
+/** @description 获取应用启动时应使用的初始语言。 */
+function getInitialLanguage() {
+ if (typeof window === 'undefined') {
+ return defaultLanguage
+ }
+
+ const persistedLanguage = window.localStorage.getItem(
+ I18N_LANGUAGE_STORAGE_KEY,
+ )
+
+ if (isSupportedLanguage(persistedLanguage)) {
+ return persistedLanguage
+ }
+
+ return detectBrowserLanguage()
+}
+
+/** @description 暴露当前应用应优先使用的语言。 */
+export function getPreferredLanguage() {
+ return getInitialLanguage()
+}
+
+/** @description 从路由路径中解析语言前缀。 */
+export function getLanguageFromPathname(pathname: string) {
+ const [, firstSegment] = pathname.split('/')
+
+ if (isSupportedLanguage(firstSegment)) {
+ return firstSegment
+ }
+
+ return null
+}
+
+void i18n.use(initReactI18next).init({
+ lng: getInitialLanguage(),
+ fallbackLng: defaultLanguage,
+ debug: false,
+ interpolation: {
+ escapeValue: false,
+ },
+ resources: {
+ 'zh-CN': {
+ common: zhCNCommon,
+ },
+ 'en-US': {
+ common: enUSCommon,
+ },
+ },
+ defaultNS: 'common',
+})
+
+/** @description 同步当前语言到 html 标签并持久化到浏览器。 */
+function syncLanguageState(language: string) {
+ if (typeof document !== 'undefined') {
+ document.documentElement.lang = language
+ }
+
+ if (typeof window !== 'undefined' && isSupportedLanguage(language)) {
+ window.localStorage.setItem(I18N_LANGUAGE_STORAGE_KEY, language)
+ }
+}
+
+syncLanguageState(i18n.resolvedLanguage ?? defaultLanguage)
+i18n.on('languageChanged', syncLanguageState)
+
+export default i18n
diff --git a/src/lib/api/api-client.ts b/src/lib/api/api-client.ts
new file mode 100644
index 0000000..6c7373f
--- /dev/null
+++ b/src/lib/api/api-client.ts
@@ -0,0 +1,212 @@
+import ky, { HTTPError, type Options, TimeoutError } from 'ky'
+import {
+ API_ERROR_MESSAGES,
+ DEFAULT_REQUEST_ACCEPT_HEADER,
+ DEFAULT_REQUEST_TIMEOUT_MS,
+} from '@/constants'
+import {
+ handleUnauthorizedSession,
+ tryRefreshAuthSession,
+} from '@/lib/auth/auth-session'
+import { useAuthStore } from '@/store/auth-store'
+
+import { ApiError } from './api-error'
+import type { ApiResponse } from './types'
+
+type RequestOptions = Omit
+type JsonRequestOptions = RequestOptions & {
+ json?: TBody
+}
+
+const AUTH_REFRESH_ATTEMPTED_CONTEXT_KEY = 'authRefreshAttempted'
+const AUTH_SKIP_REFRESH_CONTEXT_KEY = 'skipAuthRefresh'
+const appEnv = import.meta.env.VITE_APP_ENV
+const shouldLogRequests = import.meta.env.VITE_ENABLE_REQUEST_LOG === 'true'
+
+function normalizeApiBaseUrl(baseUrl: string | undefined) {
+ const candidate = baseUrl?.trim()
+
+ if (!candidate) {
+ throw new Error('VITE_API_BASE_URL 未配置')
+ }
+
+ if (/^https?:\/\//.test(candidate)) {
+ return candidate.replace(/\/+$/, '')
+ }
+
+ return candidate.replace(/^\/+/, '').replace(/\/+$/, '')
+}
+
+async function parseResponseBody(response: Response) {
+ if (response.status === 204) {
+ return null
+ }
+
+ const contentType = response.headers.get('content-type') ?? ''
+
+ if (contentType.includes('application/json')) {
+ return response.json()
+ }
+
+ return response.text()
+}
+
+function getErrorMessage(response: Response, data: unknown) {
+ if (data && typeof data === 'object') {
+ const message =
+ 'message' in data ? data.message : 'msg' in data ? data.msg : null
+
+ if (typeof message === 'string' && message.length > 0) {
+ return message
+ }
+ }
+
+ return `Request failed with status ${response.status}`
+}
+
+async function toApiError(error: unknown) {
+ if (error instanceof HTTPError) {
+ const data = error.data
+
+ return new ApiError({
+ message: getErrorMessage(error.response, data),
+ status: error.response.status,
+ data,
+ url: error.response.url,
+ })
+ }
+
+ if (error instanceof TimeoutError) {
+ return new ApiError({
+ message: API_ERROR_MESSAGES.timeout,
+ status: 408,
+ })
+ }
+
+ if (error instanceof Error) {
+ return new ApiError({
+ message: error.message,
+ })
+ }
+
+ return new ApiError({
+ message: API_ERROR_MESSAGES.unexpected,
+ })
+}
+
+export const apiBaseUrl = normalizeApiBaseUrl(import.meta.env.VITE_API_BASE_URL)
+
+const apiClient = ky.create({
+ prefix: apiBaseUrl,
+ retry: 0,
+ timeout: DEFAULT_REQUEST_TIMEOUT_MS,
+ hooks: {
+ beforeRequest: [
+ ({ request }) => {
+ request.headers.set('Accept', DEFAULT_REQUEST_ACCEPT_HEADER)
+
+ const token = useAuthStore.getState().accessToken
+
+ if (token) {
+ request.headers.set('Authorization', `Bearer ${token}`)
+ }
+
+ if (shouldLogRequests) {
+ console.info(`[api:${appEnv}] ${request.method} ${request.url}`)
+ }
+ },
+ ],
+ afterResponse: [
+ ({ request, response }) => {
+ if (shouldLogRequests) {
+ console.info(
+ `[api:${appEnv}] ${request.method} ${response.url} -> ${response.status}`,
+ )
+ }
+ },
+ ],
+ },
+})
+
+async function request(input: string, options?: Options) {
+ try {
+ const response = await apiClient(input, options)
+ const data = await parseResponseBody(response)
+
+ return data as TResponse
+ } catch (error) {
+ if (
+ error instanceof HTTPError &&
+ error.response.status === 401 &&
+ options?.context?.[AUTH_SKIP_REFRESH_CONTEXT_KEY] !== true &&
+ options?.context?.[AUTH_REFRESH_ATTEMPTED_CONTEXT_KEY] !== true
+ ) {
+ const refreshed = await tryRefreshAuthSession()
+
+ if (refreshed) {
+ return request(input, {
+ ...options,
+ context: {
+ ...options?.context,
+ [AUTH_REFRESH_ATTEMPTED_CONTEXT_KEY]: true,
+ },
+ })
+ }
+ }
+
+ if (error instanceof HTTPError && error.response.status === 401) {
+ handleUnauthorizedSession()
+ }
+
+ throw await toApiError(error)
+ }
+}
+
+async function requestRest(input: string, options?: Options) {
+ return request>(input, options)
+}
+
+export const api = {
+ get(input: string, options?: RequestOptions) {
+ return requestRest(input, {
+ ...options,
+ method: 'get',
+ })
+ },
+ post(
+ input: string,
+ { json, ...options }: JsonRequestOptions = {},
+ ) {
+ return requestRest(input, {
+ ...options,
+ json,
+ method: 'post',
+ })
+ },
+ put(
+ input: string,
+ { json, ...options }: JsonRequestOptions = {},
+ ) {
+ return requestRest(input, {
+ ...options,
+ json,
+ method: 'put',
+ })
+ },
+ patch(
+ input: string,
+ { json, ...options }: JsonRequestOptions = {},
+ ) {
+ return requestRest(input, {
+ ...options,
+ json,
+ method: 'patch',
+ })
+ },
+ delete(input: string, options?: RequestOptions) {
+ return requestRest(input, {
+ ...options,
+ method: 'delete',
+ })
+ },
+}
diff --git a/src/lib/api/api-error.ts b/src/lib/api/api-error.ts
new file mode 100644
index 0000000..e2fddd6
--- /dev/null
+++ b/src/lib/api/api-error.ts
@@ -0,0 +1,20 @@
+interface ApiErrorOptions {
+ message: string
+ status?: number
+ data?: unknown
+ url?: string
+}
+
+export class ApiError extends Error {
+ status: number | null
+ data: unknown
+ url: string | null
+
+ constructor({ message, status, data, url }: ApiErrorOptions) {
+ super(message)
+ this.name = 'ApiError'
+ this.status = status ?? null
+ this.data = data ?? null
+ this.url = url ?? null
+ }
+}
diff --git a/src/lib/api/types.ts b/src/lib/api/types.ts
new file mode 100644
index 0000000..b2ead25
--- /dev/null
+++ b/src/lib/api/types.ts
@@ -0,0 +1,6 @@
+/** @description 后端统一响应体结构。 */
+export interface ApiResponse {
+ code: number
+ msg: string
+ data: T
+}
diff --git a/src/lib/auth/auth-session.ts b/src/lib/auth/auth-session.ts
new file mode 100644
index 0000000..9a46c0c
--- /dev/null
+++ b/src/lib/auth/auth-session.ts
@@ -0,0 +1,104 @@
+import type { AuthSessionInput, AuthUser } from '@/store/auth-store'
+import { useAuthStore } from '@/store/auth-store'
+
+export type CurrentUserInitializer = () => Promise
+export type RefreshSessionHandler = (
+ refreshToken: string,
+) => Promise
+
+let currentUserInitializer: CurrentUserInitializer | null = null
+let refreshSessionHandler: RefreshSessionHandler | null = null
+let authInitializationPromise: Promise | null = null
+let refreshSessionPromise: Promise | null = null
+
+export function registerCurrentUserInitializer(
+ initializer: CurrentUserInitializer | null,
+) {
+ currentUserInitializer = initializer
+}
+
+export function registerRefreshSessionHandler(
+ handler: RefreshSessionHandler | null,
+) {
+ refreshSessionHandler = handler
+}
+
+export function isAuthenticated() {
+ const snapshot = useAuthStore.getState()
+
+ return snapshot.status === 'authenticated' && Boolean(snapshot.accessToken)
+}
+
+export function handleUnauthorizedSession() {
+ useAuthStore.getState().markUnauthorized()
+}
+
+export async function initializeAuthSession() {
+ if (authInitializationPromise) {
+ return authInitializationPromise
+ }
+
+ authInitializationPromise = (async () => {
+ await useAuthStore.persist.rehydrate()
+
+ const snapshot = useAuthStore.getState()
+
+ if (
+ !snapshot.accessToken ||
+ snapshot.currentUser ||
+ !currentUserInitializer
+ ) {
+ return
+ }
+
+ const currentUser = await currentUserInitializer()
+
+ useAuthStore.getState().setCurrentUser(currentUser)
+ })().finally(() => {
+ authInitializationPromise = null
+ })
+
+ return authInitializationPromise
+}
+
+export async function tryRefreshAuthSession() {
+ if (refreshSessionPromise) {
+ return refreshSessionPromise
+ }
+
+ const snapshot = useAuthStore.getState()
+
+ if (!snapshot.refreshToken || !refreshSessionHandler) {
+ return false
+ }
+
+ const refreshToken = snapshot.refreshToken
+
+ refreshSessionPromise = (async () => {
+ try {
+ const nextSession = await refreshSessionHandler(refreshToken)
+
+ if (!nextSession?.accessToken) {
+ handleUnauthorizedSession()
+
+ return false
+ }
+
+ useAuthStore.getState().startSession({
+ accessToken: nextSession.accessToken,
+ currentUser: nextSession.currentUser ?? snapshot.currentUser,
+ refreshToken: nextSession.refreshToken ?? snapshot.refreshToken,
+ })
+
+ return true
+ } catch {
+ handleUnauthorizedSession()
+
+ return false
+ } finally {
+ refreshSessionPromise = null
+ }
+ })()
+
+ return refreshSessionPromise
+}
diff --git a/src/lib/auth/require-auth.ts b/src/lib/auth/require-auth.ts
new file mode 100644
index 0000000..20d29f8
--- /dev/null
+++ b/src/lib/auth/require-auth.ts
@@ -0,0 +1,29 @@
+import { redirect } from '@tanstack/react-router'
+
+import type { AppLanguage } from '@/i18n'
+import { getPreferredLanguage } from '@/i18n'
+import { useAuthStore } from '@/store/auth-store'
+
+import { initializeAuthSession, isAuthenticated } from './auth-session'
+
+interface RequireAuthenticatedSessionOptions {
+ fallbackLanguage?: AppLanguage
+}
+
+export async function requireAuthenticatedSession(
+ options: RequireAuthenticatedSessionOptions = {},
+) {
+ await initializeAuthSession()
+
+ if (isAuthenticated()) {
+ return useAuthStore.getState()
+ }
+
+ throw redirect({
+ to: '/$lang',
+ params: {
+ lang: options.fallbackLanguage ?? getPreferredLanguage(),
+ },
+ replace: true,
+ })
+}
diff --git a/src/lib/head/document-metadata.ts b/src/lib/head/document-metadata.ts
new file mode 100644
index 0000000..427dec5
--- /dev/null
+++ b/src/lib/head/document-metadata.ts
@@ -0,0 +1,95 @@
+import { useEffect } from 'react'
+
+import { APP_DEFAULT_DESCRIPTION, APP_NAME } from '@/constants'
+
+interface DocumentMetadata {
+ description?: string
+ robots?: string
+ title?: string
+}
+
+function upsertMetaTag(
+ selector: string,
+ attributes: Record,
+ content: string,
+) {
+ let tag = document.head.querySelector(selector)
+
+ if (!tag) {
+ tag = document.createElement('meta')
+
+ for (const [attribute, value] of Object.entries(attributes)) {
+ tag.setAttribute(attribute, value)
+ }
+
+ document.head.append(tag)
+ }
+
+ tag.setAttribute('content', content)
+}
+
+export function buildDocumentTitle(title?: string) {
+ if (!title) {
+ return APP_NAME
+ }
+
+ return `${title} | ${APP_NAME}`
+}
+
+export function applyDocumentMetadata({
+ description = APP_DEFAULT_DESCRIPTION,
+ robots = 'index,follow',
+ title,
+}: DocumentMetadata) {
+ if (typeof document === 'undefined') {
+ return
+ }
+
+ const resolvedTitle = buildDocumentTitle(title)
+
+ document.title = resolvedTitle
+
+ upsertMetaTag(
+ 'meta[name="description"]',
+ { name: 'description' },
+ description,
+ )
+ upsertMetaTag('meta[name="robots"]', { name: 'robots' }, robots)
+ upsertMetaTag(
+ 'meta[property="og:title"]',
+ { property: 'og:title' },
+ resolvedTitle,
+ )
+ upsertMetaTag(
+ 'meta[property="og:description"]',
+ { property: 'og:description' },
+ description,
+ )
+ upsertMetaTag(
+ 'meta[property="og:site_name"]',
+ { property: 'og:site_name' },
+ APP_NAME,
+ )
+ upsertMetaTag(
+ 'meta[name="twitter:title"]',
+ { name: 'twitter:title' },
+ resolvedTitle,
+ )
+ upsertMetaTag(
+ 'meta[name="twitter:description"]',
+ { name: 'twitter:description' },
+ description,
+ )
+}
+
+export function useDocumentMetadata(metadata: DocumentMetadata) {
+ const { description, robots, title } = metadata
+
+ useEffect(() => {
+ applyDocumentMetadata({
+ description,
+ robots,
+ title,
+ })
+ }, [description, robots, title])
+}
diff --git a/src/lib/query/query-client.ts b/src/lib/query/query-client.ts
new file mode 100644
index 0000000..62eaa46
--- /dev/null
+++ b/src/lib/query/query-client.ts
@@ -0,0 +1,43 @@
+import { QueryClient } from '@tanstack/react-query'
+import {
+ QUERY_DEFAULT_GC_TIME_MS,
+ QUERY_DEFAULT_STALE_TIME_MS,
+ QUERY_RETRY_LIMIT,
+ QUERY_RETRYABLE_STATUS_CODES,
+} from '@/constants'
+
+import { ApiError } from '../api/api-error'
+
+const retryableStatusCodes = new Set(QUERY_RETRYABLE_STATUS_CODES)
+
+function shouldRetryRequest(error: unknown) {
+ if (!(error instanceof ApiError)) {
+ return true
+ }
+
+ if (error.status === null) {
+ return true
+ }
+
+ return retryableStatusCodes.has(error.status) || error.status >= 500
+}
+
+export function createQueryClient() {
+ return new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: QUERY_DEFAULT_STALE_TIME_MS,
+ gcTime: QUERY_DEFAULT_GC_TIME_MS,
+ refetchOnWindowFocus: false,
+ retry(failureCount, error) {
+ return failureCount < QUERY_RETRY_LIMIT && shouldRetryRequest(error)
+ },
+ },
+ mutations: {
+ retry: false,
+ },
+ },
+ })
+}
+
+export const queryClient = createQueryClient()
diff --git a/src/locales/en-US/common.ts b/src/locales/en-US/common.ts
new file mode 100644
index 0000000..91ab4d9
--- /dev/null
+++ b/src/locales/en-US/common.ts
@@ -0,0 +1,42 @@
+export default {
+ nav: {
+ home: 'Home',
+ },
+ shell: {
+ eyebrow: 'React SPA scaffold',
+ subtitle: 'TanStack Router + Query + Auth + i18n',
+ },
+ notFound: {
+ eyebrow: '404',
+ title: 'The page you requested could not be found.',
+ description: 'This route does not exist. Return to the scaffold home page.',
+ home: 'Back home',
+ },
+ home: {
+ eyebrow: 'Blank scaffold ready',
+ title: 'Start building your frontend project from here.',
+ description:
+ 'The template already includes routing, data fetching, session state, head metadata, and i18n. After removing the examples, this page becomes your clean starting point.',
+ cards: {
+ routingMode: 'Routing mode',
+ dataLayer: 'Data layer',
+ transport: 'Transport',
+ auth: 'Auth layer',
+ metadata: 'Page metadata',
+ },
+ values: {
+ routingMode: 'SPA + file routes',
+ dataLayer: 'TanStack Query',
+ transport: 'ky',
+ auth: 'Zustand session scaffold',
+ metadata: 'Dynamic title / meta',
+ },
+ footnote:
+ 'A practical place to start is replacing src/routes/$lang/index.tsx, src/lib/api/api-client.ts, and src/store/auth-store.ts with your project-specific structure.',
+ },
+ language: {
+ label: 'Language',
+ zhCN: '中文',
+ enUS: 'English',
+ },
+} as const
diff --git a/src/locales/zh-CN/common.ts b/src/locales/zh-CN/common.ts
new file mode 100644
index 0000000..e2914bb
--- /dev/null
+++ b/src/locales/zh-CN/common.ts
@@ -0,0 +1,42 @@
+export default {
+ nav: {
+ home: '首页',
+ },
+ shell: {
+ eyebrow: 'React SPA 脚手架',
+ subtitle: 'TanStack Router + Query + Auth + i18n',
+ },
+ notFound: {
+ eyebrow: '404',
+ title: '找不到你访问的页面。',
+ description: '当前路由不存在,你可以返回脚手架首页继续开始开发。',
+ home: '返回首页',
+ },
+ home: {
+ eyebrow: '空白脚手架已就绪',
+ title: '从这里开始搭建你的前端项目。',
+ description:
+ '模板已经接好路由、请求层、会话状态、head metadata 和多语言。删除示例后,这一页就是你的干净起点。',
+ cards: {
+ routingMode: '路由模式',
+ dataLayer: '数据层',
+ transport: '请求层',
+ auth: '认证层',
+ metadata: '页面元信息',
+ },
+ values: {
+ routingMode: 'SPA + 文件路由',
+ dataLayer: 'TanStack Query',
+ transport: 'ky',
+ auth: 'Zustand 会话基座',
+ metadata: '动态 title / meta',
+ },
+ footnote:
+ '建议先从 src/routes/$lang/index.tsx、src/lib/api/api-client.ts 和 src/store/auth-store.ts 开始替换成你的项目结构。',
+ },
+ language: {
+ label: '语言',
+ zhCN: '中文',
+ enUS: 'English',
+ },
+} as const
diff --git a/src/main.tsx b/src/main.tsx
new file mode 100644
index 0000000..a8ff6bd
--- /dev/null
+++ b/src/main.tsx
@@ -0,0 +1,31 @@
+import { QueryClientProvider } from '@tanstack/react-query'
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
+import { RouterProvider } from '@tanstack/react-router'
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import { APP_ROOT_ELEMENT_ID } from '@/constants'
+import '@/i18n'
+import { initializeAuthSession } from '@/lib/auth/auth-session'
+import { queryClient } from '@/lib/query/query-client'
+import { router } from '@/router'
+import './styles.css'
+
+const rootElement = document.getElementById(APP_ROOT_ELEMENT_ID)
+const shouldShowQueryDevtools =
+ import.meta.env.VITE_APP_ENV === 'development' &&
+ import.meta.env.VITE_ENABLE_QUERY_DEVTOOLS === 'true'
+
+if (!rootElement) {
+ throw new Error('Root element not found')
+}
+
+void initializeAuthSession()
+
+createRoot(rootElement).render(
+
+
+
+ {shouldShowQueryDevtools && }
+
+ ,
+)
diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts
new file mode 100644
index 0000000..ec7bbd6
--- /dev/null
+++ b/src/routeTree.gen.ts
@@ -0,0 +1,104 @@
+/* eslint-disable */
+
+// @ts-nocheck
+
+// noinspection JSUnusedGlobalSymbols
+
+// This file was automatically generated by TanStack Router.
+// You should NOT make any changes in this file as it will be overwritten.
+// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
+
+import { Route as rootRouteImport } from './routes/__root'
+import { Route as LangRouteRouteImport } from './routes/$lang/route'
+import { Route as IndexRouteImport } from './routes/index'
+import { Route as LangIndexRouteImport } from './routes/$lang/index'
+
+const LangRouteRoute = LangRouteRouteImport.update({
+ id: '/$lang',
+ path: '/$lang',
+ getParentRoute: () => rootRouteImport,
+} as any)
+const IndexRoute = IndexRouteImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => rootRouteImport,
+} as any)
+const LangIndexRoute = LangIndexRouteImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => LangRouteRoute,
+} as any)
+
+export interface FileRoutesByFullPath {
+ '/': typeof IndexRoute
+ '/$lang': typeof LangRouteRouteWithChildren
+ '/$lang/': typeof LangIndexRoute
+}
+export interface FileRoutesByTo {
+ '/': typeof IndexRoute
+ '/$lang': typeof LangIndexRoute
+}
+export interface FileRoutesById {
+ __root__: typeof rootRouteImport
+ '/': typeof IndexRoute
+ '/$lang': typeof LangRouteRouteWithChildren
+ '/$lang/': typeof LangIndexRoute
+}
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths: '/' | '/$lang' | '/$lang/'
+ fileRoutesByTo: FileRoutesByTo
+ to: '/' | '/$lang'
+ id: '__root__' | '/' | '/$lang' | '/$lang/'
+ fileRoutesById: FileRoutesById
+}
+export interface RootRouteChildren {
+ IndexRoute: typeof IndexRoute
+ LangRouteRoute: typeof LangRouteRouteWithChildren
+}
+
+declare module '@tanstack/react-router' {
+ interface FileRoutesByPath {
+ '/$lang': {
+ id: '/$lang'
+ path: '/$lang'
+ fullPath: '/$lang'
+ preLoaderRoute: typeof LangRouteRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ '/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
+ preLoaderRoute: typeof IndexRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ '/$lang/': {
+ id: '/$lang/'
+ path: '/'
+ fullPath: '/$lang/'
+ preLoaderRoute: typeof LangIndexRouteImport
+ parentRoute: typeof LangRouteRoute
+ }
+ }
+}
+
+interface LangRouteRouteChildren {
+ LangIndexRoute: typeof LangIndexRoute
+}
+
+const LangRouteRouteChildren: LangRouteRouteChildren = {
+ LangIndexRoute: LangIndexRoute,
+}
+
+const LangRouteRouteWithChildren = LangRouteRoute._addFileChildren(
+ LangRouteRouteChildren,
+)
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexRoute: IndexRoute,
+ LangRouteRoute: LangRouteRouteWithChildren,
+}
+export const routeTree = rootRouteImport
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes()
diff --git a/src/router.tsx b/src/router.tsx
new file mode 100644
index 0000000..30de1e6
--- /dev/null
+++ b/src/router.tsx
@@ -0,0 +1,14 @@
+import { createRouter } from '@tanstack/react-router'
+
+import { routeTree } from './routeTree.gen'
+
+export const router = createRouter({
+ routeTree,
+ defaultPreload: 'intent',
+})
+
+declare module '@tanstack/react-router' {
+ interface Register {
+ router: typeof router
+ }
+}
diff --git a/src/routes/$lang/index.tsx b/src/routes/$lang/index.tsx
new file mode 100644
index 0000000..9317e36
--- /dev/null
+++ b/src/routes/$lang/index.tsx
@@ -0,0 +1,19 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { useTranslation } from 'react-i18next'
+
+import { useDocumentMetadata } from '@/lib/head/document-metadata'
+
+export const Route = createFileRoute('/$lang/')({
+ component: HomePage,
+})
+
+function HomePage() {
+ const { t } = useTranslation()
+
+ useDocumentMetadata({
+ title: t('home.title'),
+ description: t('home.description'),
+ })
+
+ return 111
+}
diff --git a/src/routes/$lang/route.tsx b/src/routes/$lang/route.tsx
new file mode 100644
index 0000000..9f1b327
--- /dev/null
+++ b/src/routes/$lang/route.tsx
@@ -0,0 +1,46 @@
+import { createFileRoute, Navigate, Outlet } from '@tanstack/react-router'
+import { useEffect } from 'react'
+import { useTranslation } from 'react-i18next'
+
+import { getPreferredLanguage, isSupportedLanguage } from '@/i18n'
+
+export const Route = createFileRoute('/$lang')({
+ component: LanguageLayout,
+})
+
+function LanguageLayout() {
+ const { lang } = Route.useParams()
+ const { i18n } = useTranslation()
+ const preferredLanguage = getPreferredLanguage()
+ const isValidLanguage = isSupportedLanguage(lang)
+
+ useEffect(() => {
+ if (isValidLanguage && i18n.resolvedLanguage !== lang) {
+ void i18n.changeLanguage(lang)
+ }
+ }, [i18n, isValidLanguage, lang])
+
+ if (!isValidLanguage) {
+ return
+ }
+
+ // function changeLanguage(nextLanguage: AppLanguage) {
+ // const nextPathname = location.pathname.replace(
+ // /^\/(zh-CN|en-US)(?=\/|$)/,
+ // `/${nextLanguage}`,
+ // )
+ // void i18n.changeLanguage(nextLanguage)
+ // void navigate({
+ // to: nextPathname,
+ // search: location.search,
+ // hash: location.hash,
+ // replace: true,
+ // })
+ // }
+
+ return (
+ <>
+
+ >
+ )
+}
diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx
new file mode 100644
index 0000000..98025e8
--- /dev/null
+++ b/src/routes/__root.tsx
@@ -0,0 +1,64 @@
+import {
+ createRootRoute,
+ Link,
+ Outlet,
+ useLocation,
+} from '@tanstack/react-router'
+import { useEffect } from 'react'
+import { useTranslation } from 'react-i18next'
+
+import { getLanguageFromPathname, getPreferredLanguage } from '@/i18n'
+import { useDocumentMetadata } from '@/lib/head/document-metadata'
+
+function NotFoundPage() {
+ const { i18n, t } = useTranslation()
+ const location = useLocation()
+ const routeLanguage =
+ getLanguageFromPathname(location.pathname) ??
+ (i18n.resolvedLanguage || getPreferredLanguage())
+ const homePath = `/${routeLanguage}`
+
+ useEffect(() => {
+ if (
+ getLanguageFromPathname(location.pathname) &&
+ i18n.resolvedLanguage !== routeLanguage
+ ) {
+ void i18n.changeLanguage(routeLanguage)
+ }
+ }, [i18n, location.pathname, routeLanguage])
+
+ useDocumentMetadata({
+ title: t('notFound.title'),
+ description: t('notFound.description'),
+ robots: 'noindex,nofollow',
+ })
+
+ return (
+
+
+ {t('notFound.eyebrow')}
+
+
+
+ {t('notFound.title')}
+
+
+ {t('notFound.description')}
+
+
+
+
+ {t('notFound.home')}
+
+
+
+ )
+}
+
+export const Route = createRootRoute({
+ component: Outlet,
+ notFoundComponent: NotFoundPage,
+})
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
new file mode 100644
index 0000000..133d595
--- /dev/null
+++ b/src/routes/index.tsx
@@ -0,0 +1,13 @@
+import { createFileRoute, Navigate } from '@tanstack/react-router'
+
+import { getPreferredLanguage } from '@/i18n'
+
+export const Route = createFileRoute('/')({
+ component: RootRedirectPage,
+})
+
+function RootRedirectPage() {
+ return (
+
+ )
+}
diff --git a/src/store/auth-store.ts b/src/store/auth-store.ts
new file mode 100644
index 0000000..6f34b31
--- /dev/null
+++ b/src/store/auth-store.ts
@@ -0,0 +1,143 @@
+import { create } from 'zustand'
+import { createJSONStorage, persist } from 'zustand/middleware'
+
+import { AUTH_STORAGE_KEY } from '@/constants'
+
+export type AuthStatus = 'anonymous' | 'authenticated' | 'restoring'
+
+export interface AuthUser {
+ email?: string
+ id: string
+ name?: string
+ roles?: string[]
+}
+
+export interface AuthSessionInput {
+ accessToken: string
+ currentUser?: AuthUser | null
+ refreshToken?: string | null
+}
+
+interface PersistedAuthState {
+ accessToken: string | null
+ currentUser: AuthUser | null
+ refreshToken: string | null
+}
+
+interface AuthState extends PersistedAuthState {
+ clearAccessToken: () => void
+ clearSession: () => void
+ finishHydration: () => void
+ isHydrated: boolean
+ lastUnauthorizedAt: string | null
+ markUnauthorized: () => void
+ setAccessToken: (token: string) => void
+ setCurrentUser: (user: AuthUser | null) => void
+ startSession: (session: AuthSessionInput) => void
+ status: AuthStatus
+ updateTokens: (tokens: {
+ accessToken: string
+ refreshToken?: string | null
+ }) => void
+}
+
+function resolveAuthStatus(accessToken: string | null): AuthStatus {
+ return accessToken ? 'authenticated' : 'anonymous'
+}
+
+const initialPersistedState: PersistedAuthState = {
+ accessToken: null,
+ refreshToken: null,
+ currentUser: null,
+}
+
+export const useAuthStore = create()(
+ persist(
+ (set) => ({
+ ...initialPersistedState,
+ status: 'restoring',
+ isHydrated: false,
+ lastUnauthorizedAt: null,
+ setAccessToken: (token) => {
+ set({
+ accessToken: token,
+ status: 'authenticated',
+ isHydrated: true,
+ })
+ },
+ setCurrentUser: (currentUser) => {
+ set((state) => ({
+ currentUser,
+ status: resolveAuthStatus(state.accessToken),
+ }))
+ },
+ startSession: ({
+ accessToken,
+ currentUser = null,
+ refreshToken = null,
+ }) => {
+ set({
+ accessToken,
+ currentUser,
+ refreshToken,
+ status: 'authenticated',
+ isHydrated: true,
+ })
+ },
+ updateTokens: ({ accessToken, refreshToken }) => {
+ set((state) => ({
+ accessToken,
+ refreshToken: refreshToken ?? state.refreshToken,
+ status: 'authenticated',
+ isHydrated: true,
+ }))
+ },
+ finishHydration: () => {
+ set((state) => ({
+ isHydrated: true,
+ status: resolveAuthStatus(state.accessToken),
+ }))
+ },
+ clearAccessToken: () => {
+ set({
+ accessToken: null,
+ status: 'anonymous',
+ isHydrated: true,
+ })
+ },
+ clearSession: () => {
+ set({
+ ...initialPersistedState,
+ status: 'anonymous',
+ isHydrated: true,
+ })
+ },
+ markUnauthorized: () => {
+ set({
+ ...initialPersistedState,
+ status: 'anonymous',
+ isHydrated: true,
+ lastUnauthorizedAt: new Date().toISOString(),
+ })
+ },
+ }),
+ {
+ name: AUTH_STORAGE_KEY,
+ storage: createJSONStorage(() => sessionStorage),
+ partialize: (state) => ({
+ accessToken: state.accessToken,
+ currentUser: state.currentUser,
+ refreshToken: state.refreshToken,
+ }),
+ onRehydrateStorage: () => (state, error) => {
+ if (error) {
+ state?.clearSession()
+
+ return
+ }
+
+ state?.finishHydration()
+ },
+ },
+ ),
+)
diff --git a/src/styles.css b/src/styles.css
new file mode 100644
index 0000000..986eaed
--- /dev/null
+++ b/src/styles.css
@@ -0,0 +1,22 @@
+@import "tailwindcss";
+
+@theme {
+ --font-sans:
+ "Inter", "SF Pro Display", "Segoe UI", "Helvetica Neue", sans-serif;
+ --font-mono:
+ "JetBrains Mono", "SFMono-Regular", "SF Mono", Consolas, monospace;
+}
+
+@layer base {
+ html {
+ @apply h-full w-full;
+ }
+
+ body {
+ @apply m-0 h-full w-full;
+ }
+
+ #root {
+ @apply h-full w-full;
+ }
+}
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
new file mode 100644
index 0000000..89854c8
--- /dev/null
+++ b/src/vite-env.d.ts
@@ -0,0 +1,12 @@
+///
+
+interface ImportMetaEnv {
+ readonly VITE_APP_ENV: 'development' | 'production' | 'test'
+ readonly VITE_API_BASE_URL: string
+ readonly VITE_ENABLE_QUERY_DEVTOOLS: 'true' | 'false'
+ readonly VITE_ENABLE_REQUEST_LOG: 'true' | 'false'
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv
+}
diff --git a/tsconfig.app.json b/tsconfig.app.json
new file mode 100644
index 0000000..96b22d1
--- /dev/null
+++ b/tsconfig.app.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "es2023",
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
+ "module": "esnext",
+ "types": ["vite/client"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+
+ /* Linting */
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"]
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..1ffef60
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/tsconfig.node.json b/tsconfig.node.json
new file mode 100644
index 0000000..746d7c7
--- /dev/null
+++ b/tsconfig.node.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "es2023",
+ "lib": ["ES2023"],
+ "module": "esnext",
+ "types": ["node"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+
+ /* Linting */
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..64ba285
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,24 @@
+import { fileURLToPath, URL } from 'node:url'
+import babel from '@rolldown/plugin-babel'
+import tailwindcss from '@tailwindcss/vite'
+import { tanstackRouter } from '@tanstack/router-plugin/vite'
+import react, { reactCompilerPreset } from '@vitejs/plugin-react'
+import { defineConfig } from 'vite'
+
+// https://vite.dev/config/
+export default defineConfig({
+ resolve: {
+ alias: {
+ '@': fileURLToPath(new URL('./src', import.meta.url)),
+ },
+ },
+ plugins: [
+ tanstackRouter({
+ target: 'react',
+ autoCodeSplitting: true,
+ }),
+ tailwindcss(),
+ react(),
+ babel({ presets: [reactCompilerPreset()] }),
+ ],
+})