commit 3c6fd2c8cab65ff61d2938d9f5d06d315cd93509 Author: R524809 Date: Mon Oct 12 15:22:27 2026 +0800 feat: 初始化项目 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..86a63dc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4360024 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +node_modules/ +.pnpm-store/ +dist/ +dist-test/ +coverage/ +*.tsbuildinfo +*.log +.DS_Store +.env +.env.* +!.env.example +/deploy/data/ +/deploy/backups/ diff --git a/.node-version b/.node-version new file mode 100644 index 0000000..b7179ae --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +24.20.0 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..b8bcd44 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +node_modules +.pnpm-store +dist +dist-test +coverage +pnpm-lock.yaml diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..a20502b --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..3034fee --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# Doodle Store + +独立于 `doodle-mini` 的 pnpm Monorepo,用于维护统一业务后端、后续管理后台和用户 Web 网站。小程序源码与 CloudBase 兼容函数仍在 `doodle-mini`,两个项目通过 HTTPS API 协作。 + +## 当前范围 + +本次仅初始化工程,不开发具体业务: + +- `apps/server`:可运行的 NestJS 服务,只有进程健康检查。 +- `packages/contracts`:基础成功响应与健康检查类型,不包含业务字段或私有实现。 +- `apps/admin`、`apps/web`:仅预留目录与说明,尚未选择或安装前端框架。 +- 数据库、Prisma、微信登录、七牛、定时任务、业务接口与生产部署尚未接入。 + +## 本地使用 + +使用 Node.js **24.20.0** 与 pnpm **11.24.0**。如使用 fnm,可在本目录执行 `fnm use --install-if-missing`。 + +```bash +pnpm install --frozen-lockfile +pnpm build +pnpm dev +``` + +默认地址为 `http://127.0.0.1:3000/api/v1/health`。该接口只证明服务进程可以处理 HTTP 请求,不代表数据库、文件存储或业务系统已经可用。 + +可在 `apps/server/.env` 中配置 `PORT`、`HOST`,变量示例见 `apps/server/.env.example`。未配置时使用安全的本地默认值。不要提交真实 `.env` 或密钥。 + +```bash +pnpm check # 格式、代码检查、类型检查、构建与测试 +pnpm build # 按工作区依赖顺序构建 +pnpm start # 运行已构建的服务端 +pnpm format # 格式化源码与配置 +``` + +## 目录 + +```text +apps/ + server/ NestJS 服务端 + admin/ 后续管理后台,当前仅占位 + web/ 后续用户网站,当前仅占位 +packages/ + contracts/ 可公开的接口类型 +deploy/ 后续部署说明,当前未部署 +docs/ 工程初始化与接入边界说明 +``` + +每个实际应用与包声明自己的依赖。工作区内部使用 `workspace:*`,按应用独立构建、发布;不要求后台、网站与 API 同时上线。暂不引入 Turborepo、Nx、Redis、队列或微服务。 + +数据库模型、密钥和未来私有生成/绘制逻辑只能由服务端持有,不能放进公共契约包供前端导入。小程序不直接依赖本仓库源码或本地工作区包。 + +现有功能迁移的权威方案见 [docs/云服务端迁移方案.md](docs/云服务端迁移方案.md),小程序产品规划与历史设计仍保留在 `doodle-mini`。本仓库的初始化说明见 [docs/初始化说明.md](docs/初始化说明.md)。 diff --git a/apps/admin/README.md b/apps/admin/README.md new file mode 100644 index 0000000..8052dd5 --- /dev/null +++ b/apps/admin/README.md @@ -0,0 +1,5 @@ +# Admin(待开发) + +预留独立管理后台位置。本次未选择前端框架、安装依赖或开发页面。 + +后续在此添加独立 `package.json`、构建与部署配置,通过统一后端的管理 API 完成内容管理。服务端必须独立校验管理权限,不依赖前端隐藏按钮。 diff --git a/apps/server/.env.example b/apps/server/.env.example new file mode 100644 index 0000000..275ac90 --- /dev/null +++ b/apps/server/.env.example @@ -0,0 +1,3 @@ +NODE_ENV=development +HOST=127.0.0.1 +PORT=3000 diff --git a/apps/server/nest-cli.json b/apps/server/nest-cli.json new file mode 100644 index 0000000..2f068a1 --- /dev/null +++ b/apps/server/nest-cli.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true, + "tsConfigPath": "tsconfig.build.json" + } +} diff --git a/apps/server/package.json b/apps/server/package.json new file mode 100644 index 0000000..e4e6e73 --- /dev/null +++ b/apps/server/package.json @@ -0,0 +1,27 @@ +{ + "name": "@doodle/server", + "version": "0.1.0", + "private": true, + "type": "commonjs", + "scripts": { + "dev": "nest start --watch", + "build": "nest build", + "start": "node dist/main.js", + "typecheck": "tsc --noEmit -p tsconfig.json", + "test": "tsc -p tsconfig.test.json && node --test dist-test/test/*.test.js" + }, + "dependencies": { + "@doodle/contracts": "workspace:*", + "@nestjs/common": "11.2.7", + "@nestjs/core": "11.2.7", + "@nestjs/platform-express": "11.2.7", + "reflect-metadata": "0.2.2", + "rxjs": "7.8.2" + }, + "devDependencies": { + "@nestjs/cli": "11.0.24", + "@nestjs/testing": "11.2.7", + "@types/node": "24.19.2", + "typescript": "5.9.3" + } +} diff --git a/apps/server/prisma/README.md b/apps/server/prisma/README.md new file mode 100644 index 0000000..2a90e98 --- /dev/null +++ b/apps/server/prisma/README.md @@ -0,0 +1,7 @@ +# 数据库结构预留目录 + +后续用于 Prisma 数据模型与版本化数据库迁移。当前未安装 Prisma、未定义表结构,也未连接或创建数据库。 + +数据库已确认使用 PostgreSQL + Prisma;具体兼容版本在实施时验证并锁定,保留原用户和题型的字符串 ID。目标设计见 [v3.5 数据库设计文档](../../../docs/v3.5/数据库设计文档.md),当前尚未创建 Schema 或实施数据库迁移。 + +本目录及未来 Prisma Client 只能由服务端使用,不能作为公共契约包被 admin、web 或小程序导入。 diff --git a/apps/server/scripts/README.md b/apps/server/scripts/README.md new file mode 100644 index 0000000..ee0d195 --- /dev/null +++ b/apps/server/scripts/README.md @@ -0,0 +1,5 @@ +# 服务端脚本预留目录 + +后续加入 CloudBase 数据导出/导入、七牛文件迁移、关联与计数审计、备份恢复等脚本。 + +当前未执行任何迁移或外部写入。真实导出、文件和凭证不应提交源码仓库。 diff --git a/apps/server/src/app.module.ts b/apps/server/src/app.module.ts new file mode 100644 index 0000000..61bfc42 --- /dev/null +++ b/apps/server/src/app.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { HealthController } from './health/health.controller'; + +@Module({ + controllers: [HealthController], +}) +export class AppModule {} diff --git a/apps/server/src/app.setup.ts b/apps/server/src/app.setup.ts new file mode 100644 index 0000000..424a688 --- /dev/null +++ b/apps/server/src/app.setup.ts @@ -0,0 +1,6 @@ +import type { INestApplication } from '@nestjs/common'; + +export function configureApplication(app: INestApplication): void { + app.setGlobalPrefix('api/v1'); + app.enableShutdownHooks(); +} diff --git a/apps/server/src/common/runtime-options.ts b/apps/server/src/common/runtime-options.ts new file mode 100644 index 0000000..0fded4a --- /dev/null +++ b/apps/server/src/common/runtime-options.ts @@ -0,0 +1,27 @@ +export interface RuntimeOptions { + host: string; + port: number; +} + +export function getRuntimeOptions( + env: NodeJS.ProcessEnv = process.env, +): RuntimeOptions { + const rawPort = env.PORT ?? '3000'; + const port = Number(rawPort); + const host = (env.HOST ?? '127.0.0.1').trim(); + + if ( + !/^\d+$/.test(rawPort) || + !Number.isInteger(port) || + port < 1 || + port > 65535 + ) { + throw new Error('PORT must be an integer between 1 and 65535'); + } + + if (!host) { + throw new Error('HOST must not be empty'); + } + + return { host, port }; +} diff --git a/apps/server/src/health/health.controller.ts b/apps/server/src/health/health.controller.ts new file mode 100644 index 0000000..561995b --- /dev/null +++ b/apps/server/src/health/health.controller.ts @@ -0,0 +1,17 @@ +import { Controller, Get } from '@nestjs/common'; +import type { ApiSuccess, HealthStatus } from '@doodle/contracts'; + +@Controller('health') +export class HealthController { + @Get() + getHealth(): ApiSuccess { + return { + code: 0, + message: 'ok', + data: { + status: 'ok', + service: 'doodle-store-server', + }, + }; + } +} diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts new file mode 100644 index 0000000..9faf253 --- /dev/null +++ b/apps/server/src/main.ts @@ -0,0 +1,31 @@ +import 'reflect-metadata'; +import { Logger } from '@nestjs/common'; +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; +import { configureApplication } from './app.setup'; +import { getRuntimeOptions } from './common/runtime-options'; + +async function bootstrap(): Promise { + try { + process.loadEnvFile(); + } catch (error) { + if (!( + error instanceof Error && + 'code' in error && + error.code === 'ENOENT' + )) { + throw error; + } + } + + const { port, host } = getRuntimeOptions(); + const app = await NestFactory.create(AppModule); + configureApplication(app); + await app.listen(port, host); + Logger.log(`Listening on http://${host}:${port}/api/v1/health`, 'Bootstrap'); +} + +void bootstrap().catch((error: unknown) => { + Logger.error(error, undefined, 'Bootstrap'); + process.exit(1); +}); diff --git a/apps/server/src/modules/README.md b/apps/server/src/modules/README.md new file mode 100644 index 0000000..937e15e --- /dev/null +++ b/apps/server/src/modules/README.md @@ -0,0 +1,5 @@ +# 业务模块预留目录 + +后续按迁移方案逐步加入 auth、users、worksheets、categories、favorites、downloads、content、assets 和 legacy 模块。 + +当前没有创建任何业务 Controller、Service、数据库模型或伪接口。健康检查独立放在 `src/health`,不代表业务迁移已完成。 diff --git a/apps/server/test/health.test.ts b/apps/server/test/health.test.ts new file mode 100644 index 0000000..495caa6 --- /dev/null +++ b/apps/server/test/health.test.ts @@ -0,0 +1,37 @@ +import 'reflect-metadata'; +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { Test } from '@nestjs/testing'; +import { AppModule } from '../src/app.module'; +import { configureApplication } from '../src/app.setup'; + +test('服务可以启动并通过带版本前缀的 HTTP 健康检查', async (context) => { + const testingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + const app = testingModule.createNestApplication({ logger: false }); + context.after(async () => { + await app.close(); + }); + configureApplication(app); + await app.listen(0, '127.0.0.1'); + const origin = await app.getUrl(); + + const response = await fetch(`${origin}/api/v1/health`, { + signal: AbortSignal.timeout(5000), + }); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + code: 0, + message: 'ok', + data: { + status: 'ok', + service: 'doodle-store-server', + }, + }); + + const unversioned = await fetch(`${origin}/health`, { + signal: AbortSignal.timeout(5000), + }); + assert.equal(unversioned.status, 404); +}); diff --git a/apps/server/test/runtime-options.test.ts b/apps/server/test/runtime-options.test.ts new file mode 100644 index 0000000..1481d7f --- /dev/null +++ b/apps/server/test/runtime-options.test.ts @@ -0,0 +1,27 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { getRuntimeOptions } from '../src/common/runtime-options'; + +test('使用本地默认 host 和 port', () => { + assert.deepEqual(getRuntimeOptions({}), { + host: '127.0.0.1', + port: 3000, + }); +}); + +test('读取显式 host 和 port', () => { + assert.deepEqual(getRuntimeOptions({ HOST: '0.0.0.0', PORT: '4000' }), { + host: '0.0.0.0', + port: 4000, + }); +}); + +test('拒绝非法端口', () => { + for (const port of ['', '0', '65536', '-1', '3000abc', '3.5', '1e3']) { + assert.throws(() => getRuntimeOptions({ PORT: port }), /PORT/); + } +}); + +test('拒绝空 host', () => { + assert.throws(() => getRuntimeOptions({ HOST: ' ' }), /HOST/); +}); diff --git a/apps/server/tsconfig.build.json b/apps/server/tsconfig.build.json new file mode 100644 index 0000000..2b44eee --- /dev/null +++ b/apps/server/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist", "dist-test", "test"] +} diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json new file mode 100644 index 0000000..1e3976f --- /dev/null +++ b/apps/server/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "Node16", + "moduleResolution": "Node16", + "lib": ["ES2022"], + "types": ["node"], + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "esModuleInterop": true, + "sourceMap": true, + "outDir": "dist" + }, + "include": ["src/**/*.ts", "test/**/*.ts"], + "exclude": ["node_modules", "dist", "dist-test"] +} diff --git a/apps/server/tsconfig.test.json b/apps/server/tsconfig.test.json new file mode 100644 index 0000000..03bb522 --- /dev/null +++ b/apps/server/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "outDir": "dist-test", + "sourceMap": false + } +} diff --git a/apps/web/README.md b/apps/web/README.md new file mode 100644 index 0000000..c64da08 --- /dev/null +++ b/apps/web/README.md @@ -0,0 +1,5 @@ +# Web(待开发) + +预留面向国内中文家长的用户网站位置。本次未选择前端框架、安装依赖或开发页面。 + +后续根据 SEO 需求确定 SSR/预渲染方案,添加独立 `package.json`、构建与部署配置,复用统一后端 API。私有练习纸生成逻辑不导入浏览器代码。 diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..a5c71fa --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,7 @@ +# 部署预留目录 + +本次未部署服务、启动数据库或修改云服务器。 + +后续加入 Docker Compose、Nginx、数据库备份与恢复配置,按应用独立部署;数据库不开放公网端口,真实环境变量不提交仓库。 + +在增加数据库、admin、SSR Web 或渲染进程前检查现有 2 核 4 GB 服务器资源,不在生产长期运行整套开发模式。 diff --git a/docs/v3.5/数据库设计文档.md b/docs/v3.5/数据库设计文档.md new file mode 100644 index 0000000..48a7e8f --- /dev/null +++ b/docs/v3.5/数据库设计文档.md @@ -0,0 +1,401 @@ +# Doodle Store v3.5 数据库设计 + +> 数据库:PostgreSQL;ORM:Prisma。数据库选型已按用户确认更新。 +> +> 状态:目标设计,尚未创建数据库、Prisma Schema 或业务迁移;线上集合、数据量和异常分布待 S0 盘点。 +> +> 更新日期:2026-10-12。 +> +> 配套方案:[云服务端迁移方案](../云服务端迁移方案.md)。本文细化该方案的数据结构与一致性规则。 + +## 1. 范围与依据 + +首期迁移现有微信小程序后端,统一使用 PostgreSQL 主数据库、NestJS API 和七牛文件存储。小程序仍在 `doodle-mini` 维护,数据库模型与迁移只放在 `doodle-store/apps/server/prisma`。Web 与管理后台通过 API 访问,不直接连接数据库。 + +设计优先级:用户本次确认的 PostgreSQL 选型 → 当前迁移方案 → 实际云函数及调用代码 → 历史设计。当前代码有 7 个核心集合;新增 3 张辅助表用于文件、调度和审计。 + +| 阅读依据 | 与建模相关的实际行为 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | +| [旧云数据库设计](../../../doodle-mini/docs/小程序云开发方案和数据库设计.md)、[旧后端预案](../../../doodle-mini/docs/后端部署方案.md) | 历史模型参考;其中 MySQL Schema、`favorites_log` 和生成器必填字段不直接沿用 | +| [userLogin](../../../doodle-mini/cloudfunctions/userLogin/index.js)、[userSettings](../../../doodle-mini/cloudfunctions/userSettings/index.js) | OpenID 登录、UnionID 可空;用户包含 `totalDownloads`;清资料与注销是不同操作 | +| [userFavorites](../../../doodle-mini/cloudfunctions/userFavorites/index.js)、[userDownloadLogs](../../../doodle-mini/cloudfunctions/userDownloadLogs/index.js) | 实际收藏集合为 `favorites`;下载支持 `exportId`;缺失题型时详情为 null;当前关系写入与计数未纳入同一事务 | +| [题型发布](../../../doodle-mini/cloudfunctions/worksheetsPublish/index.js)、[批量修改](../../../doodle-mini/cloudfunctions/worksheetsBatchPatch/index.js)、[题型查询](../../../doodle-mini/cloudfunctions/worksheetsQuery/index.js) | `path`、`grade`、三种状态、真实/seed 计数、内容更新时间均需保留;重发题型可能覆盖计数,迁移后修正 | +| [页面构建](../../../doodle-mini/cloudfunctions/pageContentBuild/index.js)、[自动刷新](../../../doodle-mini/cloudfunctions/homeAutoRefresh/index.js)、[开关](../../../doodle-mini/cloudfunctions/homeTimerControl/index.js) | `page_configs/current` 为权威配置;`settings/homeAutoRefresh` 为开关;静态文件是派生产物 | +| [分龄推荐](../../../doodle-mini/cloudfunctions/ageRecommendedQuery/index.js) | 5 个年龄段;筛选、seed、去重、榜单及不足补齐规则应保留 | +| [保存流程](../../../doodle-mini/miniprogram/utils/downloadPrint.ts)、[设置页面](../../../doodle-mini/miniprogram/supportPages/settings/settings.ts) | 实际保存成功后才上报;页面承诺注销清除收藏和下载记录,但旧云函数只删除用户 | + +首期不建反馈、学习路线、会员、支付、孩子档案、学习包或服务端生成任务表。`feedback`、`learning_plans` 等若在线上盘点中存在,应先归档并明确处理范围,不能当作空集合丢弃。年龄目标和周计划目前主要来自小程序配置,动态推荐不需要新建推荐关系表。 + +## 2. 全局约定 + +### 2.1 命名、主键与类型 + +| 项目 | 约定 | +| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| 表名 / 数据库列名 | 表名沿用集合名、列名 snake_case;Prisma 字段使用 camelCase,通过 `@map` / `@@map` 映射 | +| 原主键 | 7 张现有表 `_id → id` 原值保留,使用 `varchar(128)`;不强制转换 UUID,不批量重发 ID | +| 新主键 | 新用户、关系、日志及辅助记录可由服务端生成 UUID 字符串,仍存 `varchar(128)`;配置/设置保留固定业务 ID | +| 大小写 | ID、OpenID、exportId、对象 key 必须精确匹配;这些列使用确定性、区分大小写的排序规则,迁移 SQL 明确指定 `COLLATE "C"`;外键列保持一致 | +| 字符编码 | 数据库 UTF-8;文本字段按 Unicode 字符处理;导入先检查实际长度,超长项报告,禁止静默截断 | +| 时间 | `timestamptz(3)`,连接会话设 UTC;API 输出 ISO 8601 UTC;业务日与任务计划使用 `Asia/Shanghai` | +| JSON | `jsonb`;Prisma 使用 `Json` / `Json?` 和 `@db.JsonB`;需要查询、唯一约束或计数的字段使用结构化列 | +| 空值 | 可选文本空串按字段规则转 SQL NULL;0 与 NULL 严格区分;JSON nullable 字段区分 SQL NULL 与 JSON null | +| 整数 | 年龄/难度/年级用 smallint,排序用 integer,累计计数与版本用 bigint;禁止浮点计数 | +| bigint 对外 | 首期计数/版本 DTO 保持 number;转换前验证处于 JS 安全整数范围,超出返回明确错误;Prisma BigInt 不直接 JSON 序列化 | +| 默认值 | 新建记录使用默认值;迁入记录显式写入原值,不能用默认值覆盖已有 0、false 或时间 | + +本文字段表中“可空”指 SQL NULL;无另行说明的字段均 NOT NULL。表长度是目标限制,正式 DDL 前用全量导出核对;超过限制时调整设计或隔离,不直接裁剪。 + +### 2.2 关系与删除策略 + +```mermaid +erDiagram + users ||--o{ favorites : "用户外键" + users ||--o{ download_logs : "用户外键" + users o|--o{ assets : "所有者外键" + categories o|--o{ categories : "父分类外键" + categories ||--o{ worksheets : "分类外键" + worksheets ||--o{ favorites : "题型逻辑关联" + worksheets ||--o{ download_logs : "题型逻辑关联" + assets o|--o{ users : "头像外键" + assets o|--o{ worksheets : "预览外键" + page_configs ||--o{ job_runs : "执行结果逻辑关联" +``` + +- 收藏和下载的 `user_id` 有真实外键,`ON DELETE RESTRICT`;注销服务先处理关系与计数,再删除用户,避免级联删除跳过业务逻辑。 +- 收藏和下载的 `worksheet_id` **保留原 ID,但不建题型外键**,以支持历史缺失题型;读取使用 LEFT JOIN。新写入由服务校验题型存在且 active,使用行锁保证状态检查与写入一致。 +- `worksheets.category_id` 与 `categories.parent_id` 有外键且删除 RESTRICT;分类循环由服务在串行化分类树修改的事务中检查。`subcategory` 仍是业务字符串,不强制认作分类主键。 +- 题型首期仅通过 draft/active/hidden 管理,不提供硬删除 API。历史缺失题型不创建伪题型补齐;收藏/历史中非 active 题型不返回未发布详情,允许返回 `worksheet: null` 并取消收藏。 +- 用户头像、题型预览使用 assets 外键,删除 RESTRICT;资产所有者外键可 `ON DELETE SET NULL`。对象存储删除通过业务清理重试完成,不由数据库级联触发。 +- 页面 JSON 引用、任务/审计中的资源 ID 是逻辑关联。审计操作者不建用户外键,避免注销删除审计记录;用户注销时移除其中的身份关联与个人信息。 + +## 3. 现有业务表 + +### 3.1 users — 用户 + +| 数据库列 / Prisma 字段 | 类型 | 可空 / 默认 | 说明 | +| -------------------------------- | -------------- | ----------- | -------------------------------------------------------------------------------- | +| id | varchar(128) | 否 | 原 `users._id`;新账户独立生成 ID | +| mini_app_id / miniAppId | varchar(64) | 否 | 当前微信 AppID;旧数据根据来源环境填入,不由客户端指定 | +| openid | varchar(128) | 否 | 微信服务端返回的可信标识 | +| unionid | varchar(128) | 是 | 空串转 NULL;仅保存身份线索,首期不自动合并账户 | +| nick_name / nickName | varchar(100) | 是 | 用户昵称 | +| avatar_asset_id / avatarAssetId | varchar(128) | 是 | assets.id;API 动态生成 avatarUrl,不保存临时签名 URL | +| total_downloads / totalDownloads | bigint | 否 / 0 | 保留旧字段,作为历史兼容值;当前云函数创建后没有随下载维护,不能充当权威累计统计 | +| role | varchar(16) | 否 / user | CHECK:user、admin;迁入默认 user,管理员由可信名单显式配置 | +| status | varchar(16) | 否 / active | CHECK:active、disabled;禁用账户不接受写入 | +| auth_version / authVersion | integer | 否 / 1 | 非负凭证版本;主动失效时递增,JWT 带版本且请求核对当前值 | +| created_at / createdAt | timestamptz(3) | 否 / now() | 保留首次创建时间 | +| last_active_at / lastActiveAt | timestamptz(3) | 否 / now() | 保留活跃时间;登录更新 | +| updated_at / updatedAt | timestamptz(3) | 否 / now() | 新系统资料/权限等变更时间;旧数据缺失按导入规则补齐 | + +约束与索引:主键 id;UNIQUE `(mini_app_id, openid)`;普通索引 unionid;CHECK `total_downloads >= 0`、`auth_version >= 0`。UnionID 不设唯一约束,避免尚未实施跨应用绑定时阻止两个 AppID 的独立账户;区别于旧预案中的全局唯一 UnionID。 + +`GET /me/downloads/count` 与资料页数量以 `download_logs` 的当前用户记录数为准,不读取旧 totalDownloads。若兼容 DTO 需要该字段,由适配层返回查询结果;原字段只用于迁移对账,不形成第二套计数。 + +### 3.2 categories — 分类 + +| 数据库列 / Prisma 字段 | 类型 | 可空 / 默认 | 说明 | +| --------------------------- | -------------- | ----------- | ------------------------------------------------------------------ | +| id | varchar(128) | 否 | 原分类 ID,如 math、chinese;不重命名 | +| name | varchar(100) | 否 | 分类名 | +| icon | varchar(1024) | 否 | 保留图标名、字符等非文件内容;若是文件则转为稳定资产引用,接口解析 | +| icon_asset_id / iconAssetId | varchar(128) | 是 | 文件类型图标的 assets 外键;存在时对外 icon 由此解析 | +| color | varchar(32) | 否 | 保留颜色值 | +| sort_order / sortOrder | integer | 否 / 0 | 保留排序权重 | +| parent_id / parentId | varchar(128) | 是 | 自关联,根分类为 NULL,空串规范化 | +| created_at / createdAt | timestamptz(3) | 否 / now() | 旧代码不写该字段,缺失用导入批次时间并报告 | +| updated_at / updatedAt | timestamptz(3) | 否 / now() | 新系统更新维护 | + +主键 id;索引 `(parent_id, sort_order, id)`;CHECK `parent_id IS NULL OR parent_id <> id`。父分类必须存在,导入按树层级排序;多层循环不能靠上述 CHECK 判断。删除被题型或子分类引用的分类返回冲突。 + +### 3.3 worksheets — 题型 + +| 数据库列 / Prisma 字段 | 类型 | 可空 / 默认 | 说明 | +| ------------------------------------- | -------------- | ----------- | ----------------------------------------------------------------------- | +| id | varchar(128) | 否 | 原 `_id` / 本地 worksheet ID | +| title | varchar(100) | 否 | 保留标题;旧文档的 ≤8 字是展示建议 | +| subtitle | varchar(500) | 否 | 保留副标题;不使用旧预案 desc 字段 | +| category_id / categoryId | varchar(128) | 否 | 原 category;API 仍输出 category | +| subcategory | varchar(100) | 是 | 子类字符串,旧空串可转 NULL;兼容 DTO 可还原空串 | +| path | varchar(1024) | 否 | 完整小程序页面路径及查询参数,保留跳转/分享语义;不使用 legacyPage 替代 | +| age_min / ageMin | smallint | 否 | 最小年龄 | +| age_max / ageMax | smallint | 否 | 最大年龄 | +| grade | smallint | 否 / 0 | 保留负值,如 -4 托班、0 幼小衔接;不使用 unsigned | +| difficulty | smallint | 否 | CHECK 1~4 | +| preview_asset_id / previewAssetId | varchar(128) | 否 | 七牛资产或经核实的已有 HTTPS 资源;API 唯一预览字段仍为 previewImg | +| tags | jsonb | 否 / [] | 字符串数组;数据库检查顶层 array,应用校验每个元素为字符串 | +| is_new / isNew | boolean | 否 / false | 新品标记 | +| is_hot / isHot | boolean | 否 / false | 热门标记 | +| sort_order / sortOrder | integer | 否 / 0 | 题型列表排序 | +| downloads | bigint | 否 / 0 | 原始下载计数;迁入原值,不按历史日志条数重算 | +| likes | bigint | 否 / 0 | 原始收藏计数;旧异常先报告,新业务保持事务一致性 | +| downloads_seed / downloadsSeed | bigint | 否 / 0 | 运营展示种子;旧 API downloads_seed 由兼容层映射 | +| likes_seed / likesSeed | bigint | 否 / 0 | 运营展示种子 | +| status | varchar(16) | 否 / draft | CHECK:draft、active、hidden | +| created_at / createdAt | timestamptz(3) | 否 / now() | 首次创建,不随重新发布修改 | +| content_updated_at / contentUpdatedAt | timestamptz(3) | 是 | 原值保留;缺失时展示回退 createdAt,不伪造一次内容更新 | +| updated_at / updatedAt | timestamptz(3) | 否 / now() | 任意业务修改,包括计数和状态;与内容更新时间区分 | + +约束:`age_min >= 0 AND age_max >= age_min`;`difficulty BETWEEN 1 AND 4`;四种计数均非负;`jsonb_typeof(tags) = 'array'`。非法旧数据先隔离报告,不能直接归零掩盖问题。 + +首期索引:`(category_id, status, sort_order, updated_at DESC, id)`、`(status, sort_order, updated_at DESC, id)`、`(status, age_min, age_max)`。榜单对 active 候选集按真实值 + seed 排序,先不建全部榜单表达式索引或 tags GIN 索引;后续用真实查询计划决定。 + +`template`、`generator`、`generatorConfig`、`layoutConfig`、`userConfigurable` 尚非当前云函数核心字段,不设首期必填列。如果导出中存在,保存原始快照并明确转换;服务端生成正式实施时再设计私有配置,禁止通过公共题型 DTO 暴露。 + +### 3.4 favorites — 当前收藏关系 + +| 数据库列 / Prisma 字段 | 类型 | 可空 / 默认 | 说明 | +| -------------------------- | -------------- | ----------- | ------------------------- | +| id | varchar(128) | 否 | 原 `_id`;新关系生成新 ID | +| user_id / userId | varchar(128) | 否 | users.id 外键 | +| worksheet_id / worksheetId | varchar(128) | 否 | 保留原题型 ID,逻辑关联 | +| created_at / createdAt | timestamptz(3) | 否 / now() | 收藏时间 | + +UNIQUE `(user_id, worksheet_id)`;列表索引 `(user_id, created_at DESC, id DESC)`。这是当前关系表,取消即删除,不是收藏事件日志。 + +旧重复关系选择最早 createdAt 的记录作为保留项,同时间按 id 精确排序;被合并的原 ID 写入报告。迁移不借去重自动改写历史 likes。 + +### 3.5 download_logs — 成功保存历史 + +| 数据库列 / Prisma 字段 | 类型 | 可空 / 默认 | 说明 | +| -------------------------- | -------------- | ----------- | ----------------------------------------------------- | +| id | varchar(128) | 否 | 原 `_id` 或新日志 ID | +| user_id / userId | varchar(128) | 否 | users.id 外键 | +| worksheet_id / worksheetId | varchar(128) | 否 | 原题型 ID,逻辑关联 | +| export_id / exportId | varchar(128) | 是 | 同一次成功保存的事件 ID;历史缺失为 SQL NULL | +| source | varchar(16) | 否 / mini | CHECK:mini、legacy、import;新版上报、旧桥、历史迁入 | +| params | jsonb | 是 | 保留历史可选参数快照;当前 add 不写,不新增收集要求 | +| created_at / createdAt | timestamptz(3) | 否 / now() | 原历史时间 / 新服务收到事件的时间 | + +UNIQUE `(user_id, export_id)` 使用 PostgreSQL 默认 NULL 语义,允许同用户多条 export_id=NULL;**不使用 NULLS NOT DISTINCT**。空串先转 NULL,避免旧记录冲突。新版 API 必填 exportId,NULL 只允许历史导入。重复非空 exportId 若指向不同题型,隔离处理,不能任意挑一条。 + +索引 `(user_id, created_at DESC, id DESC)`、`(worksheet_id, created_at DESC, id DESC)`。不同 exportId 的同题型多次保存都是有效事件;禁止 UNIQUE `(user_id, worksheet_id)` 或按秒级时间窗口去重。 + +### 3.6 page_configs — 页面配置权威源 + +| 数据库列 / Prisma 字段 | 类型 | 可空 / 默认 | 说明 | +| ------------------------------------ | -------------- | ----------- | ------------------------------------------------------------------------- | +| id | varchar(128) | 否 | 首期沿用 current;主键,不额外限制只能一行,其他旧文档需盘点 | +| home | jsonb | 是 | 首页快照,保持 featured / hot / sections / categoryTabs / ageBands 等结构 | +| category | jsonb | 是 | 保留旧分支兼容,当前 home/all 构建会清为 NULL | +| age | jsonb | 是 | 保留旧分支兼容,目前通常 NULL | +| version | bigint | 否 / 0 | 保留旧版本,每次成功提交配置 +1,CHECK 非负 | +| updated_at / updatedAt | timestamptz(3) | 否 / now() | 原 ISO 字符串转换;API 还原 ISO 字符串 | +| published_version / publishedVersion | bigint | 是 | 可选静态配置文件最后成功发布的版本,缺失为 NULL | +| published_at / publishedAt | timestamptz(3) | 是 | 可选静态文件发布成功时间 | + +首期只需主键。JSON 字段非空时顶层必须是 object。数据库快照中的文件字段转为 `{ "assetId": "..." }` 对象,公共 DTO 再解析为原 `previewImg` / icon 等字符串;普通路径、图标字符和文本不转换。所有嵌套路径在迁移映射清单中记录。 + +```json +{ + "featured": [ + { + "id": "原题型ID", + "title": "练习纸", + "previewImg": { "assetId": "预览资产ID" }, + "path": "/原页面路径?原参数", + "available": true + } + ], + "hot": [], + "sections": [] +} +``` + +示例仅展示内部引用方式,导入必须保留完整原结构,不按示例删减字段。API 返回 home/category/age/version/updatedAt;不向普通客户端公开内部资产元数据或发布状态。 + +### 3.7 settings — 运行配置 + +| 数据库列 / Prisma 字段 | 类型 | 可空 / 默认 | 说明 | +| ---------------------- | -------------- | ----------- | ------------------------------------------------------- | +| id | varchar(128) | 否 | 原固定 ID,例如 homeAutoRefresh | +| value | jsonb | 否 | 原文档除 `_id` 和 updatedAt 外的业务字段;必须为 object | +| version | bigint | 否 / 0 | 新增配置并发版本,CHECK 非负 | +| updated_at / updatedAt | timestamptz(3) | 否 / now() | 原值优先,缺失采用批次时间 | + +仅主键。`homeAutoRefresh` 迁入示例为 `{ "enabled": false }`,不把 false 覆盖成默认 true。缺文档时业务默认 enabled=true;S1 初始化时创建该设置行,不在写事务内临时缺行加锁。 + +新计划可在 value 中加入 `timezone: "Asia/Shanghai"` 和 schedule;具体执行时刻必须先核对旧控制台时区和执行记录。`GET/PATCH /admin/home/auto-refresh` 由服务映射为原 enabled 形状,未知线上设置逐项盘点,不仅迁移这一行。 + +## 4. 新增辅助表 + +### 4.1 assets — 文件引用与迁移映射 + +| 数据库列 / Prisma 字段 | 类型 | 可空 / 默认 | 说明 | +| ------------------------------- | -------------- | ------------ | ---------------------------------------------------------------------- | +| id | varchar(128) | 否 | 稳定资产 ID | +| source_ref / sourceRef | text | 是 | 原 cloud:// fileID 或已核实的稳定 HTTPS URL;禁止签名 URL | +| source_ref_hash / sourceRefHash | char(64) | 是 | source_ref 的 SHA-256 十六进制,用于定长唯一索引 | +| provider | varchar(16) | 否 / qiniu | CHECK:qiniu、external;后者用于已有可用 HTTPS 资源 | +| bucket | varchar(128) | 是 | 七牛空间名;qiniu 就绪时必填 | +| object_key / objectKey | text | 是 | 七牛对象 key,区分大小写,使用版本化路径 | +| object_ref_hash / objectRefHash | char(64) | 是 | 对 provider、bucket、objectKey 的规范数组序列化结果计算 SHA-256 | +| external_url / externalUrl | text | 是 | external 时的受控稳定 HTTPS 地址,不支持任意客户端远程抓取 | +| purpose | varchar(32) | 否 | CHECK:avatar、preview、category_icon、font、share、page_config、other | +| owner_user_id / ownerUserId | varchar(128) | 是 | 用户头像归属;公共运营资产可空 | +| visibility | varchar(16) | 否 | CHECK:public、private;头像使用 private | +| mime_type / mimeType | varchar(128) | 是 | 识别的媒体类型 | +| size_bytes / sizeBytes | bigint | 是 | 文件大小,非负 | +| checksum | char(64) | 是 | 文件内容 SHA-256;不能直接以对象 ETag 当 SHA-256 | +| status | varchar(16) | 否 / pending | CHECK:pending、ready、failed、deleting、deleted | +| last_error / lastError | text | 是 | 经清理的迁移/删除失败原因 | +| created_at / createdAt | timestamptz(3) | 否 / now() | 创建映射或上传时间 | +| updated_at / updatedAt | timestamptz(3) | 否 / now() | 状态变化时间 | + +UNIQUE source_ref_hash、UNIQUE object_ref_hash(可空);索引 `(owner_user_id, purpose, status)`、`(status, updated_at)`。hash 命中时仍核对完整源/对象引用,避免错误归并;checksum 不唯一,同内容可有不同归属和访问策略。 + +qiniu ready 必须具备 bucket、objectKey、objectRefHash、mimeType、sizeBytes 和 checksum;external ready 必须有经核实的 externalUrl,无法取得内容摘要时在报告注明。源文件下载、上传与校验完成后才 ready,再写业务外键。DB 存引用,访问域名从服务配置取;私有签名 URL 在响应时生成,不能写回数据库或页面配置。 + +上传和对象存储操作不放在长数据库事务中;先完成上传并登记资产,再短事务绑定业务记录。未绑定文件由清理流程回收,清理前检查 users、worksheets、categories 和 page_configs 的所有引用。旧桥复制云文件后也必须遵循同一路径。 + +### 4.2 job_runs — 调度与执行结果 + +| 数据库列 / Prisma 字段 | 类型 | 可空 / 默认 | 说明 | +| ---------------------------- | -------------- | ------------ | ----------------------------------------------------------------- | +| id | varchar(128) | 否 | 执行记录 ID | +| job_name / jobName | varchar(64) | 否 | 如 home_auto_refresh、page_config_publish | +| run_key / runKey | varchar(128) | 否 | 每个逻辑运行唯一:定时按上海业务日;手动按请求 ID;发布按目标版本 | +| trigger_type / triggerType | varchar(16) | 否 | CHECK:scheduled、manual、recovery | +| status | varchar(16) | 否 / pending | CHECK:pending、running、succeeded、failed、skipped | +| scheduled_at / scheduledAt | timestamptz(3) | 是 | 计划执行时刻,存 UTC | +| started_at / startedAt | timestamptz(3) | 是 | 实际开始时间 | +| finished_at / finishedAt | timestamptz(3) | 是 | 完成/失败/跳过时间 | +| attempt | integer | 否 / 0 | 领取尝试次数,非负 | +| lock_token / lockToken | varchar(128) | 是 | 每次领取新 token,避免过期执行者提交 | +| locked_until / lockedUntil | timestamptz(3) | 是 | 租约截止时间 | +| result | jsonb | 是 | 版本、数量、跳过理由等结构化摘要 | +| error_message / errorMessage | text | 是 | 清理敏感信息后的错误摘要 | +| created_at / createdAt | timestamptz(3) | 否 / now() | 建立记录时间 | +| updated_at / updatedAt | timestamptz(3) | 否 / now() | 最近状态修改 | + +UNIQUE `(job_name, run_key)`;索引 `(status, locked_until)`、`(job_name, created_at DESC)`。一日任务逻辑运行仅一条记录,失败恢复沿用同一记录并增加 attempt,不靠插入新记录绕过唯一键。 + +记录唯一键解决逻辑重复,不能代替内容写锁。领取使用条件更新或行锁;提交结果必须核对 lockToken 和租约。失败不标 succeeded;禁用记录 skipped;重启后补跑当日已到计划时间、未成功的任务,关闭开关时不补跑。数据库事务内只写数据,不等待上传。 + +### 4.3 admin_audit_logs — 管理审计 + +| 数据库列 / Prisma 字段 | 类型 | 可空 / 默认 | 说明 | +| ---------------------------- | -------------- | ----------- | ------------------------------------------------- | +| id | varchar(128) | 否 | 审计 ID | +| actor_user_id / actorUserId | varchar(128) | 是 | 操作时的管理员 ID,逻辑关联;注销时去关联 | +| actor_type / actorType | varchar(16) | 否 | CHECK:admin、system、migration | +| action | varchar(64) | 否 | 发布、状态变更、计数修订、分类更新、开关等动作 | +| resource_type / resourceType | varchar(64) | 否 | worksheets、categories、page_configs、settings 等 | +| resource_id / resourceId | varchar(128) | 是 | 目标主键;批量操作按每个实际修改项分别记录 | +| request_id / requestId | varchar(128) | 否 | 用于聚合一次批量操作与排障 | +| before_data / beforeData | jsonb | 是 | 变更前的必要字段,尤其真实/seed 计数 | +| after_data / afterData | jsonb | 是 | 变更后字段 | +| reason | text | 是 | 管理修订说明;显式统计修订必须提供 | +| created_at / createdAt | timestamptz(3) | 否 / now() | 操作提交时间 | + +索引 `(resource_type, resource_id, created_at DESC)`、`(actor_user_id, created_at DESC)`、request_id。已提交管理变更与审计在同一事务;dryRun 不写业务表,可记录校验日志;失败请求写应用错误日志,不制造成功审计。快照不得包含 token、AppSecret、session_key、登录 code、签名 URL 或不必要个人资料。 + +## 5. 一致性与事务规则 + +### 5.1 登录与注销 + +登录用可信 `(miniAppId, openid)` 查找用户,沿用老 ID。首次并发登录以唯一约束兜底,冲突后读取已创建用户。首期 JWT 使用 userId + authVersion;每次鉴权核对用户存在、active 和版本,role 以当前服务端数据为准。 + +涉及用户的写事务先锁用户行并核对状态,再锁题型行;多个题型按 ID 固定顺序加锁。收藏、下载、资料修改和注销遵守相同锁顺序,避免注销与新写入交叉。 + +清除资料:昵称与头像引用置 NULL,保留用户、收藏和历史;解除引用后清理用户私有文件。注销:锁用户 → 对所有收藏涉及的现存题型按 ID 加锁并减少 likes → 删除收藏、下载日志 → 解除头像及其他私有资产关联,标记清理 → 清理身份审计关联 → 删除用户,全部数据库修改一事务。用户不存在时旧 token 立即无效。注销后再次微信登录创建全新 userId,不复用已删除账户,不恢复旧记录。 + +历史下载计数是题型累计成功保存次数,注销删除个人日志时不回减 downloads。likes 表示当前收藏数量,注销需要扣除当前关系的贡献;若旧计数小于应扣数量,扣减保护为 0,并在同事务记录系统审计异常,不能得到负值,也不能因此阻止注销。 + +对象清理在事务提交后重试,失败状态可查询。数据库备份有滚动保留期限,恢复时须重新落实备份之后的注销删除;不能用恢复或旧导入重新激活已注销关系。具体备份保留期和清理任务在部署文档落实。 + +### 5.2 收藏 + +添加:锁用户、锁题型并校验 active → `INSERT ... ON CONFLICT (user_id, worksheet_id) DO NOTHING RETURNING id` → 仅实际插入时 likes+1、更新 updatedAt → 提交。重复添加返回现有关系,不加数。 + +取消:锁用户、锁现存题型 → `DELETE ... RETURNING id` → 仅实际删除且题型存在时减少 likes → 提交。允许取消已下架或历史缺失题型的收藏。旧数据导致 likes 已为 0 时维持 0 并记录异常。重复取消不减数。 + +### 5.3 成功保存与 exportId + +新版只在相册保存成功后生成/复用 exportId 上报。事务先锁用户、查已有 `(userId, exportId)`:同题型返回既有记录,题型不同返回冲突。尚不存在时锁题型并校验 active,插入日志,真实 downloads+1,更新 updatedAt 后提交;唯一键同时防并发重试。已有事件在题型随后下架时仍可幂等返回。 + +`ON CONFLICT DO NOTHING RETURNING` 未插入时不能加计数,需在后续查询读取已有结果并核对题型。死锁或序列化冲突采用有界重试,始终复用相同 exportId,禁止事务失败后单独补加计数。 + +旧桥未收到 exportId 时为本次接收请求生成带 legacy 前缀的 ID,内部转发重试复用;不同旧请求生成不同 ID,承认无法精确识别独立旧请求间的同一次导出。历史导入的 NULL exportId 保持 NULL,不给每条旧日志伪造客户端事件 ID。 + +### 5.4 题型发布与计数 + +常规发布只更新内容列、contentUpdatedAt 和 updatedAt;createdAt、真实计数与 seed 保留。新建题型真实计数为 0,运营种子单独写入。修改年龄、标签等内容需更新内容时间;纯计数或上下架只更新 updatedAt。 + +真实计数/seed 修订使用明确管理动作,锁题型行、记录前后值和 reason、同事务写审计。批量操作每项独立事务,保留逐项结果;dryRun 做同样校验且不写入。不将整批部分失败伪装为完全成功。 + +展示 likes/downloads = 原始值 + 对应 seed;管理 rawStats 不加 seed。保持现有首页 Top5、分龄 Top3 下载 + Top3 收藏再全量下载补齐的种子参与规则。分龄严格采用 `ageMin == 下限 AND ageMax >= 上限`;分龄并列沿用 updatedAt DESC,再以 id 稳定排序。首页并列新增稳定 id 排序,并在固定样本验收中记录差异。 + +### 5.5 首页配置与自动任务 + +首次初始化 current 行和 homeAutoRefresh 设置行。手动构建、状态引起的重建、自动刷新都使用同一内容服务:按固定顺序锁 homeAutoRefresh 设置行 → 锁 current 行 → 基于最新快照校验/合并 → version+1 → 写配置、任务结果和必要审计 → 提交。管理开关也锁同一设置行,确保事务边界明确。 + +首期题型量小,可在短事务中完成查询与 JSON 构建;若构建移出事务,提交前比较 expectedVersion,冲突必须重新读取重建,不能覆盖新配置。自动刷新只覆盖 featured/hot,保留 sections 等手动配置。home/all 构建清 category/age,category 分支只重建 category;原调用中空数组被当作“沿用现有配置”的行为由兼容层保留。 + +公共 API 直接读取 DB 配置。若兼容期仍发布静态 JSON,提交后用 `page_config_publish` 执行记录重试,文件采用按版本不可变对象 key;publishedVersion 只在对应版本上传成功后前移,防止旧发布覆盖新版本。签名 URL 不进入公共静态配置,私有头像也不进入公共页面 JSON。静态发布失败不回滚已经提交的业务配置,保留上一可用文件并明确发布状态。 + +## 6. CloudBase 导入与审计 + +### 6.1 字段转换 + +| 旧集合 / 字段 | PostgreSQL 落点 | 处理 | +| -------------------------------------- | ------------------------------ | ---------------------------------------------------- | +| 7 个核心集合 `_id` | 各表 id | 精确保留大小写和原值 | +| users.openid / unionid | openid / unionid | 增补可信 miniAppId,空 UnionID 转 NULL | +| users.avatarUrl | users.avatar_asset_id + assets | 复制/核实文件,保留 sourceRef 映射;无头像为 NULL | +| users.totalDownloads | total_downloads | 保留原值,统计接口不以它为准 | +| categories.icon | icon / icon_asset_id | 区分字符图标和文件;文件走资产映射 | +| worksheets.category / previewImg | category_id / preview_asset_id | 分类校验;预览转换为稳定资产引用 | +| worksheets.likes_seed / downloads_seed | likes_seed / downloads_seed | 原值保存;缺失才补 0 | +| favorites | favorites | 使用真实集合名,不从旧文档虚构 favorites_log | +| download_logs.exportId | export_id | 有值保留、空串或缺失转 NULL;params 若存在保留 | +| page_configs.home/category/age | 相应 jsonb | 深层遍历全部文件引用;保留字段、数组顺序和 null 语义 | +| page_configs.updatedAt | updated_at | 旧 ISO 时间转为 timestamptz | +| settings.enabled 等业务字段 | settings.value | 保留所有配置,例 enabled=false 原样迁入 | + +完整原始导出与逐项转换报告存受控迁移归档,不把整个含身份信息的原文档塞入公开扩展 JSON。未知字段逐项列出保留位置或明确处理结果,不能默默忽略。 + +### 6.2 顺序与异常 + +1. 冻结/导出按迁移方案进行,保存全量分页、原主键清单、计数、时间范围及摘要。对 BSON 日期/ISO 日期规范化;非法日期隔离,不用当前时间偷偷代替。 +2. 检查字段长度、类型、状态、年龄、难度、计数、分类树及用户唯一性。旧缺失 createdAt/updatedAt 仅对允许补齐的新增审计字段使用固定批次时间;业务原始日期无法确定则明确隔离/评审。 +3. 建表并初始化配置锁行;先导用户及分类(父节点优先),再导资产并确认 ready,绑定头像/分类图标,再导题型、收藏、下载、页面配置和设置。用户头像先 NULL 后绑定,解决用户与资产双向引用。 +4. 收藏/下载缺失用户时隔离归档,不新建已注销用户;缺失题型时保留日志原 ID,LEFT JOIN 返回空详情。题型缺失分类需隔离修复,不伪造分类。 +5. 重复 `(AppID, OpenID)` 不自动合并用户,先报告全部身份与关系;重复收藏按确定规则合并;重复 exportId 同题型按最早记录归并,不同题型隔离。每次合并记录原 ID 和保留 ID。 +6. 保留题型原始计数与 seed,报告其与收藏/日志数的基线差异,不在导入过程中“纠正”为条数。事务一致性从切换后的新业务开始建立。 +7. dryRun 完成后按主键 upsert;导入期间不触发业务加数、自动刷新或内容上新。试导必须使用隔离数据库;最终导入目标不得混入测试写入。 +8. 最终切换后新数据库成为唯一写入源,禁止再次用旧快照 upsert 覆盖;反向恢复必须包括新增、修改和删除,不只追加记录。 + +每集合满足 `原始数 = 正常导入数 + 合并重复数 + 隔离数`,并提供主键/文件映射与异常原因。辅助表单独对账,不与旧业务总数混算。NULL exportId 的多条历史下载都应正常保留。 + +## 7. Prisma 与数据库迁移落地 + +- datasource 使用 `provider = "postgresql"`。具体 PostgreSQL 主版本、Prisma 版本、Node 运行时组合在 S1 样板中验证并锁定,不照抄旧 MySQL Schema 或旧连接参数。 +- PostgreSQL 类型映射:字符串 `@db.VarChar(n)`、计数 BigInt `@db.BigInt`、日期 DateTime `@db.Timestamptz(3)`、JSON `@db.JsonB`。SQL 的 now() 负责新建时间;更新由服务显式维护,内容时间不依赖自动 updatedAt 推断。 +- PostgreSQL CHECK、标识符列排序规则、DESC 索引和本文事务 SQL 以版本化迁移 SQL 为最终落点;Prisma Schema 同步描述可表达的约束。业务验证不能代替数据库唯一键。 +- `apps/server/prisma/schema.prisma`、`migrations/` 与迁移说明同版本提交;本文是实施输入,本次不生成看似已执行的 Schema/迁移文件,也不连接生产库。 +- 新增列采用可兼容上一 API 版本的步骤,数据回填和收紧 NOT NULL/外键分开验证;应用回滚继续使用新主库。正式环境不使用 db push 随意覆盖结构。 +- 原始导出、隔离记录、文件映射、导入报告和恢复校验随迁移批次归档。数据库备份使用 PostgreSQL 工具链,并实际演练恢复;备份目标在服务器之外。 +- 首期不分表、不分区、不引入 Redis、队列或全文搜索。限制连接池与导入并发,按 2 核 4 GB 实际联调结果配置,禁止所有应用默认建立无限连接。 + +## 8. 数据库验收清单 + +| 场景 | 验收结果 | +| ---------------------------------------- | --------------------------------------------------------------------------- | +| 老用户登录 / 同 OpenID 不同 AppID | 老 userId 不变;跨 AppID 不自动合并;大小写不同 ID 不冲突 | +| 并发第一次登录 | 唯一用户,不产生两套关系 | +| 重复/并发收藏、取消、事务中途失败 | 只随真实关系增删计数;失败后关系和计数都回滚 | +| 同 userId + exportId 并发重报 | 仅一条日志、downloads 仅 +1;不同题型冲突可识别 | +| 同题型不同 exportId / 多条 NULL 历史事件 | 有效事件均保留,不错误合并 | +| 重新发布、状态变更、管理员修订 | 累计值不意外清零;三种时间用途正确;修订有同事务审计 | +| 清资料 / 注销与写入竞争 | 清资料保留关系;注销删除个人关系、旧 token 失效且不新增孤儿;文件失败可重试 | +| 原用户缺失 / 原题型缺失 | 用户孤儿隔离;题型缺失历史保留,列表可读、无未发布详情泄露 | +| 页面分支、seed 和分龄榜单 | 固定样本字段、顺序、筛选、去重、补齐与旧行为一致,新增并列规则显式记录 | +| 自动/手动同时刷新、开关修改、任务重启 | 不覆盖手动栏目、不重复成功运行;旧任务停用;上海业务日正确 | +| 文件和嵌套资源引用 | 七牛对象实际存在、摘要可核对;DB 无临时签名 URL;正式资源不依赖 cloud:// | +| 重复试导与最终导入对账 | 原 ID 稳定;计数不被触发加数;正常/合并/隔离可解释;无测试数据混入 | +| bigint、nullable JSON 与时区 | DTO 可序列化;NULL 不变 0 或 false;日期与配置版本完整保留 | +| 备份恢复 / 新写入后的反向转换 | 最新增改删及注销均落实;不复活已取消收藏或已注销用户 | + +S0 仍需确认:全部线上集合及配置文档、来源 AppID、历史字段长度与类型、计数异常、文件归属及清单、旧调度时区/执行记录。上述未知项不改变 PostgreSQL 决策;发现不兼容数据时更新转换报告和设计,再实施 DDL。 diff --git a/docs/云服务端迁移方案.md b/docs/云服务端迁移方案.md new file mode 100644 index 0000000..3fce178 --- /dev/null +++ b/docs/云服务端迁移方案.md @@ -0,0 +1,471 @@ +# Doodle Mini 现有功能迁移到云服务端方案 + +> 决策:已确认迁移到自有腾讯云服务器;数据库使用 PostgreSQL。 +> +> 文档归属:本方案在 `doodle-store/docs` 中维护,作为迁移实施的唯一方案文档;小程序产品规划与历史设计仍保留在 `doodle-mini`。 +> +> 工程组织:小程序继续在独立项目 `doodle-mini` 中维护;新项目 `doodle-store` 使用 pnpm workspace 管理服务端、管理后台和 Web,不包含小程序源码。 +> +> 实施状态:已完成基础工程初始化;业务功能、数据迁移与上线尚未实施。本文不是迁移完成报告。 +> +> 更新日期:2026-10-12。以 `doodle-mini` 当前仓库代码为迁移依据,线上数据量、文件清单及服务器环境需在实施第一步核实。 + +## 1. 目标与范围 + +本次将现有小程序的业务后端、数据、文件及定时任务迁出微信 CloudBase,形成 **一套业务 API、一套主数据库、一套文件存储**。后续 Web 直接复用这套后端。 + +采用轻量模块化单体,不建立微服务、数据库集群或复杂运维平台。AI 辅助接口、脚本、适配和部署配置开发,人工及实际运行验证负责确认行为、数据和切换结果。 + +### 1.1 本次交付 + +- 微信静默登录、用户资料、清除资料和注销账户。 +- 题型与分类查询、分龄推荐、首页配置、手动及自动推荐刷新。 +- 收藏、下载历史、个人数量统计、题型下载/收藏计数。 +- 现有题型发布、上下架、分类管理、批量属性同步等管理能力。 +- 头像、预览图、云字体和首页预拉取配置的迁移与访问。 +- 小程序 HTTP 接入、短期旧版本兼容、数据迁移、部署、备份、验收与回滚。 + +### 1.2 后续单独实施 + +- 独立管理后台 `doodle-store/apps/admin` 的完整页面;本次先迁移管理 API,并保留小程序现有管理操作,不以新后台上线作为切换前置条件。 +- 完整 Web 网站、SEO 内容页和 Web 登录/账户绑定。 +- 将存量 Canvas 题型全部改为服务端生成。 +- 创意涂色、批量 PDF、会员、支付、孩子档案和学习包等新功能。 + +本次迁移后,现有随机出题、Canvas 绘制、相册保存、微信分享、广告解锁及本地下载配额仍按小程序现有方式工作。迁移后端并不会自动隐藏已经随小程序发布的生成代码。未来 Web 的私有生成器另行接入同一后端。 + +## 2. 已知条件与实施前核实项 + +| 项目 | 当前已知条件 | 方案处理 | +| ---------- | ----------------------------------------------------- | ----------------------------------------------------------- | +| 云服务器 | 腾讯云,地区可能为华东;2 核、4 GB 内存、60 GB 系统盘 | 作为初期 API 与数据库部署目标;实施前检查现有业务和可用资源 | +| 服务器期限 | 已付费至 2027 年 10 月,计划长期保留 | 不将整台服务器续费全部归于本项目,但记录实际资源占用 | +| 流量 | 每月 500 GB | 用于 API 和经服务器上传的流量;七牛直出文件单独计费 | +| 域名 | 已持有长期域名 | 实施前确认具体 API/资源子域名、备案、DNS 和 HTTPS | +| 文件存储 | 计划使用七牛 | 新文件统一进入七牛;存储、请求和下载费用按实际用量核算 | +| CloudBase | 当前 19.9 元/月 | 过渡期保留;完全退出兼容函数与旧文件依赖后再停止套餐 | +| 后端规模 | 17 个云函数,入口 JS 合计约 1,875 行 | 主要是查询和 CRUD,适合 AI 辅助迁移;行数不等于全部迁移成本 | + +实施前应取得:服务器系统/现有服务/可用内存与磁盘/峰值带宽;域名备案和小程序后台配置权限;现有 AppID 对应的 AppSecret;CloudBase 导出与文件访问权限;七牛空间、区域和 HTTPS 资源域名。密钥通过环境变量配置,不写进文档或仓库。 + +这些信息用于实施,不影响现在确定架构。备案、审核、DNS 和文件传输等待时间与实际开发工时分开记录。 + +## 3. 目标架构与技术选择 + +```mermaid +flowchart TD + Mini[新版微信小程序 · doodle-mini] -->|HTTPS| Nginx[Nginx] + Web[后续 Web · doodle-store] -->|HTTPS| Nginx + Admin[后续管理后台 · doodle-store] -->|HTTPS| Nginx + Old[旧版小程序] --> Bridge[过渡期 CloudBase 兼容函数] + Bridge -->|签名请求| Nginx + Nginx --> API[统一业务 API · NestJS] + API --> DB[(PostgreSQL · 唯一主数据库)] + API --> Files[七牛 · 文件存储] + API --> Wx[微信登录接口] + Mini -->|图片和字体访问| Files + Scheduler[应用内定时任务] --> API + Render[后续私有生成模块] -.-> API +``` + +CloudBase 兼容函数只转发旧协议,不保留独立业务规则;切换后不再写旧数据库。未来 Web 的页面渲染与练习纸绘制使用同一后端数据,生成任务增长后可拆为独立进程,不需要再建一套业务系统。 + +| 层级 | 首期选择 | 选择理由与限制 | +| ---------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| 服务端 | TypeScript + NestJS,单应用 | 延续仓库技术栈和已有后端预案;按模块组织现有业务 | +| 工程管理 | 独立 `doodle-store` 项目 + pnpm workspace Monorepo | 管理 `apps/server`、后续 `apps/admin` / `apps/web` 和必要共享包;`doodle-mini` 不加入工作区 | +| 数据库 | PostgreSQL + Prisma | 用唯一键和事务保证收藏/下载一致性;版本与 ORM 兼容性在实施时验证 | +| 运行时 | 受维护的 Node.js LTS | 实施时锁定具体版本及依赖,不沿用旧文档的版本号 | +| 文件 | 七牛对象存储,HTTPS 自定义资源域名 | 与数据库分离;数据库保存对象引用,避免保存会过期的签名 URL | +| 部署 | Docker Compose 管理 API 与 PostgreSQL,Nginx 反向代理 | 固定镜像版本、持久化数据库卷;已有 Nginx 可复用,不覆盖其他业务 | +| 定时任务 | 单实例应用内调度 + 数据库执行记录/锁 | 任务少,不引入 Redis/队列;控制重复执行及手动刷新竞争 | +| 日志与监测 | 结构化日志、健康检查、错误与任务失败通知 | 覆盖基本排障,不额外部署大型监控平台 | + +2 核 4 GB 可作为起步配置,但承载能力以实际资源检查和联调结果为准。先部署 API 与数据库;服务端绘制的内存/CPU 需求在后续样板中单独测量。数据库不开放公网端口。 + +### 3.1 两个独立项目的职责 + +- `doodle-mini`:继续维护现有微信小程序、微信平台适配、原管理页面及过渡期 CloudBase 兼容函数;HTTP 接入改造也在此项目完成。保持现有小程序目录和开发工具配置,不搬入新项目。 +- `doodle-store`:独立项目位于 `/Users/joey-xd/sites/doodle/doodle-store`,采用 pnpm workspace Monorepo;`apps/server` 承载统一业务 API,后续 `apps/admin` 和 `apps/web` 分别提供管理后台与用户网站。当前只完成基础工程与健康检查,尚无业务 API。 +- 两个项目通过有版本的 HTTPS API 协作,独立管理依赖、版本与发布;不使用跨仓库源码导入、相对路径引用或本地软链接作为正式依赖。 +- Monorepo 只用于代码、依赖与开发任务管理,不改变“一套业务 API、一套主数据库”的目标,也不要求使用 NestJS 自身的多应用 Monorepo 模式。 + +### 3.2 doodle-store 计划目录与工作区 + +已初始化 pnpm 工作区、`apps/server` 的 NestJS 骨架和 `packages/contracts` 的基础响应/健康检查类型;`apps/admin`、`apps/web`、Prisma、迁移脚本与部署目录目前仅有用途说明。以下为目标结构,业务模块、数据库接入、完整前端和部署配置尚未实现,不为凑齐目录提前开发完整应用。 + +```text +doodle-store/ + apps/ + server/ # 首期统一业务 API + src/ + prisma/ # 数据模型与版本化迁移,仅服务端访问 + scripts/ # 数据/文件导入、审计与恢复 + package.json + .env.example # 变量名示例,不包含真实密钥 + admin/ # 后续独立管理后台 + web/ # 后续用户网站与 SEO 页面 + packages/ + contracts/ # 必要的公开请求/响应类型及枚举 + deploy/ # Compose、Nginx、备份配置 + docs/ # 新系统接口与部署文档 + pnpm-workspace.yaml + pnpm-lock.yaml + package.json +``` + +`doodle-store/pnpm-workspace.yaml`: + +```yaml +packages: + - 'apps/*' + - 'packages/*' +``` + +- 每个应用与共享包有独立 `package.json`,声明自身依赖;根项目设为 `private: true`,主要负责工作区脚本和公共开发工具,不集中安装所有应用的运行依赖。 +- `doodle-store` 使用单一 `pnpm-lock.yaml`,锁定 `packageManager` 和 Node.js 版本;CI 使用冻结锁文件安装。`doodle-mini` 保持自身依赖和构建流程,不因新项目使用 pnpm 而强制切换。 +- 工作区内部包使用 `workspace:*` 引用,不要求发布到 npm;`packages/contracts` 仅用于新项目内部共享公开契约,不包含数据库模型、密钥或私有生成器实现。 +- 使用 pnpm 的筛选与递归脚本执行构建、检查、测试;应用构建需包含其依赖共享包的构建步骤。先采用 pnpm 原生能力,不默认引入 Turborepo、Nx、私有包仓库或复杂发布平台。 +- TypeScript 基础配置可以复用,但 Node.js、浏览器和 SSR 应用保留各自运行环境配置;UI、SDK 等共享包仅在出现真实复用需求后提取,不建设无边界的通用 `shared` 包。 + +`doodle-store/apps/server` 内部目标结构(当前仅实现启动配置与健康检查,业务模块待开发): + +```text +apps/server/ # 以下路径相对于 doodle-store + src/ + health/ # 已实现:仅 HTTP 进程存活检查 + modules/ # 以下业务模块尚未实现,当前仅用途说明 + auth/ # 微信登录、用户身份、管理权限 + users/ # 用户资料与注销 + worksheets/ # 题型、上下架和属性同步 + categories/ # 分类 + favorites/ # 收藏与计数 + downloads/ # 成功保存记录、历史与计数 + content/ # 首页配置、推荐和发布 + assets/ # 文件上传、引用和访问地址 + legacy/ # 旧云函数协议适配,过渡后移除 + common/ # 校验、错误格式、日志 + prisma/ # 数据结构、版本化迁移 + scripts/ # 导入、文件迁移、审计与恢复 + package.json # 服务端独立依赖和构建脚本 + .env.example # 变量名示例,不包含真实密钥 +``` + +### 3.3 跨项目契约、发布与代码边界 + +- 公共 API 契约以 `doodle-store` 为权威来源,提供版本化接口文档/OpenAPI。小程序不能直接使用另一个仓库的 `workspace:*` 包;首期可将按契约生成或核对的必要类型同步到 `doodle-mini`,记录来源版本,并由小程序服务层继续使用 `wx.request` / `wx.uploadFile`。 +- 公开契约变化时,分别核对 `doodle-mini`、`apps/admin`、`apps/web` 的适配与测试结果。新增兼容字段优先;字段删除、更名或语义变化须保留兼容期,不能假设两个项目或全部客户端同时升级。 +- `apps/server`、`apps/admin`、`apps/web` 独立构建和部署。后台若采用 SPA,可发布静态文件;Web 是否采用 SSR/预渲染另行选择。共享包变化时检查所有受影响应用,但不要求所有应用一起发布。 +- 数据库结构、访问凭证、管理权限判断、未来私有生成/绘制实现仅由服务端持有。管理后台和 Web 通过 API 访问,不直接导入 Prisma Client 或服务端源码;前端包也不得间接依赖这些私有模块。`private: true` 不是防止代码被前端打包的安全边界。 +- 服务端和各前端分别管理环境变量,真实密钥不提交仓库、不写入前端可公开配置。生产仅部署目标应用及必要依赖,不在 2 核 4 GB 服务器上长期运行整套开发模式或无关应用。 + +## 4. 现有功能与云函数迁移清单 + +以下 API 是目标契约,实施时生成接口文档。统一前缀为 `/api/v1`;表中使用省略前缀的路径。用户接口从可信登录态取得 userId,不接受客户端指定身份。管理接口一律由服务端校验管理员权限。 + +| 当前云函数 | 当前功能 | 迁移目标 | 关键要求 | +| ------------------------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | +| `userLogin` | 静默登录、更新昵称头像 | `POST /auth/wechat/login`、`GET /me`、`PATCH /me/profile` | 原用户 ID 不变;AppSecret/session_key 不下发 | +| `userSettings` | 清除资料、注销 | `DELETE /me/profile`、`DELETE /me` | 清资料保留收藏/历史;注销落实页面承诺的关联数据删除 | +| `userFavorites` | 添加/取消/列表/数量/单张检查/批量检查 | `PUT/DELETE /me/favorites/:worksheetId`、`GET /me/favorites`、`GET /me/favorites/count`、`POST /me/favorites/check` | 收藏关系唯一;真实增删与题型计数同一事务 | +| `userDownloadLogs` | 添加成功保存记录、列表、数量 | `POST /me/downloads`、`GET /me/downloads`、`GET /me/downloads/count` | 新版必须传 exportId;只在实际保存成功后上报 | +| `worksheetsQuery` | 题型查询、状态筛选、分类状态数量、展示计数 | `GET /worksheets`、`GET /worksheets/:id`、`GET /admin/worksheets` | 普通用户只看 active;管理查询支持全部状态和原始计数 | +| `worksheetsPublish` | 按固定 ID 新增或更新题型 | `PUT /admin/worksheets/:id` | 保留发布能力;普通内容更新不重置累计计数 | +| `worksheetsUpdateStatus` | draft/active/hidden 切换 | `PATCH /admin/worksheets/:id/status` | 状态变更后刷新受影响首页展示 | +| `worksheetsBatchPatch` | 批量年龄、年级、标签、种子及计数同步 | `POST /admin/worksheets/batch-patch` | 保留 dryRun、逐项结果和错误;计数修订单独审计 | +| `worksheetsStatsUpdate` | 直接递增 likes/downloads | 不向普通用户提供通用加数接口;由收藏/下载服务承接 | 当前仓库工具无调用方;旧协议仅在确认必要时适配,不能绕过幂等 | +| `categoriesQuery` | 分类排序查询 | `GET /categories` | 保留 ID、父子关系、图标、颜色和排序 | +| `categoriesCreate` | 创建分类 | `POST /admin/categories` | ID 唯一;校验父分类和循环引用 | +| `categoriesUpdate` | 更新分类 | `PUT /admin/categories/:id` | 不修改已有主键;分类关系可审计 | +| `ageRecommendedQuery` | 5 个年龄段推荐 | `GET /recommendations/age?ageKey=3-4` | 保留筛选、Top3 下载 + Top3 收藏、去重及不足补齐规则 | +| `pageConfigFetch` | 读取 current 页面配置 | `GET /page-config` | 保留 home/category/age、version、updatedAt 和 previewImg | +| `pageContentBuild` | 手动首页配置、栏目与展示重建 | `POST /admin/page-config/build`、`GET /admin/page-config` | 保留 featuredIds/hotIds/sections 与现有 page 分支语义 | +| `homeAutoRefresh` | 自动更新首页下载/收藏榜 | 服务端任务、`POST /admin/home/refresh` | 保留各 Top5 和其他首页配置;启停、时区、重复执行可核对 | +| `homeTimerControl` | 开关自动推荐 | `GET/PATCH /admin/home/auto-refresh` | 数据从新数据库读写,替代管理页数据库直读 | + +### 4.1 不依赖云函数名称也必须迁移的部分 + +本节代码位置均属于 `doodle-mini`;接入改造留在原项目,新业务实现位于 `doodle-store/apps/server`。 + +| 当前依赖 | 代码位置 | 迁移处理 | +| -------------------------------- | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | +| 云初始化、静默登录、首页配置加载 | `miniprogram/app.ts` | 改为 API 配置与登录;预拉取、缓存、兜底保持可用 | +| 云调用与资料缓存 | `miniprogram/utils/auth.ts` | `wx.login` + HTTP;资料缓存与有效登录态分开管理 | +| 收藏/下载业务包装 | `miniprogram/utils/favorites.ts`、`downloadLogs.ts` | 替换内部实现,保留页面使用方式和统计触发时机 | +| 分类与分龄直接调用 | `pages/category/category.ts`、`pages/age/age.ts` | 接入业务服务层,保持排序/推荐/路由与字段兼容 | +| 头像上传 | `pages/profile/profile.ts` | 替换 `wx.cloud.uploadFile`,存入七牛 | +| 预览图上传与题型发布 | `base/pageMixin.ts` | API 上传 + 管理发布;开发环境隐藏入口不等于管理授权 | +| 云文件字体地址 | `core/font/fontLoader.ts`、`fontProfiles.ts` | 改成 HTTPS 文件引用;继续使用 `wx.loadFontFace` 并验证字形 | +| 内置云预览地址 | `pages/home/home.data.ts` | 迁移并替换 cloud://;兼顾本地兜底数据 | +| 管理页云函数调用 | `supportPages/categoryManage`、`categoryContentManage`、`homeContentManage`、`worksheetSync`、`settings` | 接入管理/用户 API,逐页核对 | +| 管理页直读 settings | `supportPages/homeContentManage/homeContentManage.ts` | 改为管理 API;仅换云函数封装无法覆盖此处 | +| 旧适配器与 HTTP 工具 | `platform/cloud-adapter.ts`、`utils/http.ts` | 逐调用点收口;前者目前指向 doodle,不能假设全站均经它调用 | +| 小程序后台预拉取 URL | 仓库之外的微信后台配置 | 改为新公共配置地址,关闭调试绕过后验证 | +| 云开发定时触发配置 | `cloudfunctions/homeAutoRefresh/config.json` 与控制台 | 核实实际执行时间/时区,迁入调度,切换时停旧任务 | + +`wx.reportEvent` 是微信统计能力,不依赖业务 CloudBase 数据库,无需为了迁移而更换。分享、广告和相册权限也继续使用微信能力。 + +## 5. API、身份和统计约定 + +### 5.1 新接口与兼容格式 + +新 API 使用统一响应,例如: + +```json +{ + "code": 0, + "message": "ok", + "data": {}, + "requestId": "request-id" +} +``` + +- HTTP 状态码区分参数错误、未登录、无权限、冲突、服务异常;失败不包装成成功的空列表。 +- 列表提供分页和总量。当前一次最多取 100 条的实现不能成为数据库迁移后的隐形上限;小程序服务层负责拉完所需内容或采用分页展示。 +- 新服务内部使用 `id`;小程序 DTO/兼容桥继续提供旧 `_id`、`path`、`previewImg`。数据库路径字段可命名 `miniProgramPath`,Web 路径后续另做映射。 +- 云函数目前存在 `{code:0,data}` 与 `{success:true,data,stats}` 两种协议,由业务服务层/兼容桥转换,不要求所有页面同时改写数据形状。 +- `previewImg` 是唯一预览字段;公共 DTO 不包含生成器私有配置、用户身份密钥或未发布内容。 + +### 5.2 微信登录与用户延续 + +1. 小程序调用 `wx.login()` 获取一次性 code。 +2. 服务器用配置的 AppID/AppSecret 调用微信 `code2Session`,取得可信 OpenID,UnionID 可为空。 +3. 按当前 AppID + OpenID 查找迁入用户,沿用原 `users._id`;确实不存在时才新建。 +4. 签发有期限的应用登录凭证。首期可用 JWT,建议初始有效期 24 小时;过期后通过微信静默登录续用,暂不增加独立刷新令牌体系。 +5. 每次用户请求核对账户有效状态;角色从服务端配置/数据取得。注销立即使旧凭证失效。 + +客户端缓存的 user 对象、OpenID、UUID 都不能作为可信登录凭证。请求遇到 401 时合并并发登录,成功后最多重试一次;写请求需具备幂等性。当前 7 天资料缓存不能替代 API 身份验证。 + +Web 用户不会凭现有小程序 OpenID 自动变成同一账户。跨端身份绑定作为后续业务设计,当前保留稳定 userId 和可选 UnionID,不自动合并用户。 + +管理权限首期采用明确管理员名单或角色字段,默认不授权。发布、上下架、批量修订和定时任务配置必须服务端验证;不以客户端 `envVersion`、按钮隐藏或自报角色为依据。 + +### 5.3 成功保存与收藏计数 + +- 保存成功的判定继续是 `wx.saveImageToPhotosAlbum` 成功回调;点击保存、图片生成完成、拿到文件、接口返回 200 都不是成功保存。 +- 保存成功后递增本地配额、记录 `export_success`,用同一个 exportId 向 API 写下载记录。 +- 数据库对 `(userId, exportId)` 建唯一键,下载记录和 `worksheets.downloads + 1` 在同一事务内完成。重试只返回已有结果,不再次加数。 +- API 上报失败不能把已保存的图片判定为保存失败。新版可用小型本地补报队列重试同一个 exportId;补报不再次递增本地配额或发送 export_success。队列绑定原 userId,换账户不代报,注销时清除。 +- 服务端接收的是客户端成功回报,无法独立证明图片已进入手机相册;本次不把该回报设计成支付凭据。 +- 收藏唯一键为 `(userId, worksheetId)`;确实新增/删除关系时才更新 likes。重复请求不增减;注销删除收藏时同步处理受影响计数。 +- 展示计数与真实计数分开:展示可沿用真实值 + `likes_seed`/`downloads_seed`,分析和管理原始查询不加 seed。迁移期间不改变已有推荐的种子参与规则。 +- 常规重新发布题型不能将真实计数重置为 0。显式统计修订保留管理员入口,但记录操作者及前后值。 + +旧版缺少 exportId 的保存请求不能凭题型 ID 或几秒时间窗口去重,这会误删用户真实多次保存。兼容桥按每次收到的旧请求生成事件 ID,内部转发重试复用该 ID;接受无法跨独立旧请求精确去重的限制,并监测其调用量。 + +## 6. 数据迁移 + +数据库详细设计见 [v3.5 数据库设计文档](./v3.5/数据库设计文档.md),使用 PostgreSQL + Prisma,覆盖以下集合及辅助表。 + +### 6.1 集合映射与主键 + +代码实际使用 7 个核心集合。历史设计文档中的 `favorites_log` 与当前代码不一致,应迁移实际 `favorites`;`feedback`、`learning_plans` 等只出现在设计中的集合,先盘点线上是否存在,不默认当成已上线功能。 + +| CloudBase 集合 | 新表 | 迁移要求 | +| --------------- | --------------- | ----------------------------------------------------------------------------- | +| `users` | `users` | `_id → id` 原值保留;OpenID、可选 UnionID、资料、创建/活跃时间及旧字段保留 | +| `categories` | `categories` | ID、parentId、图标、颜色、排序保留 | +| `worksheets` | `worksheets` | ID、路由、年龄、grade、难度、tags、状态、预览、真实/seed 计数及各时间字段保留 | +| `favorites` | `favorites` | 用户/题型关联不变;重复关系按审计规则归并 | +| `download_logs` | `download_logs` | ID、用户/题型关联、时间、可选 exportId 保留;历史缺 exportId 可为空 | +| `page_configs` | `page_configs` | `current` ID、JSON 结构、版本、时间保留;所有嵌套文件引用转换 | +| `settings` | `settings` | 包括 `homeAutoRefresh.enabled`,先盘点全部线上配置 | + +新增必要辅助表:文件迁移/引用表 `assets`、任务执行记录 `job_runs`、管理审计记录 `admin_audit_logs`。它们用于迁移与运行,不代表已有业务集合。 + +主键采用足够长度的字符串,保留大小写语义,不重新生成所有 ID。业务字段用结构化列,tags/页面配置/扩展配置用 JSON;导入前保留完整原始导出,未识别字段进入迁移报告,不能默默丢弃。 + +主要索引:用户 `(miniAppId, openid)` 唯一;收藏 `(userId, worksheetId)` 唯一及 `(userId, createdAt)`;下载 `(userId, exportId)` 唯一及用户/题型时间索引;题型分类/状态/排序索引。空 exportId 规范为 NULL,历史多条无 ID 下载不得互相冲突。 + +### 6.2 导入与审计规则 + +1. 完整分页导出实际集合,记录每个集合总数、主键清单、时间范围和校验摘要;不可只导出首批 100 条。 +2. 规范 BSON/JSON 日期为同一 UTC 时间,数据库和 API 时间一致;业务日与任务时区明确使用 Asia/Shanghai。空值与 0 区分,禁止因默认值覆盖已有数据。 +3. 先导分类和用户,再导题型、收藏、下载、配置及设置。脚本可重复执行、按主键 upsert,并支持 dryRun;生产切换后不允许再次用旧导出覆盖新写入。 +4. 检测重复 OpenID、重复收藏、重复 exportId、失联关联、非法状态/年龄、缺失图片和未知字段。异常输出逐项报告,不以“跳过错误”当成功。 +5. 原用户已经注销而留下的收藏/下载记录应隔离归档,不能重建已注销用户。题型缺失时保留历史 worksheetId 和记录,列表 LEFT JOIN 返回空详情,不能整个历史接口报错。 +6. 收藏去重、异常隔离等转换明确写进审计报告:原始数 = 正常导入数 + 合并重复数 + 隔离数。不得无说明要求转换后条数与原始条数相同。 +7. 保留历史真实计数及 seed;旧计数与历史条数可能已有差异,先报告,不直接按日志总数重算下载量。切换后的新事件用事务建立一致性。 + +验收既对总量,也对用户集合、主键和关联。重点核对若干有收藏/历史的老用户、无资料用户、缺失题型记录和多个年龄段,不只抽查首页。 + +## 7. 文件、首页预拉取与定时任务 + +### 7.1 文件迁移 + +资源清单同时覆盖云数据库、页面配置嵌套对象、字体配置、首页内置兜底、用户头像和云存储目录。现有 HTTPS 资源(如部分 `cdn.joeyone.cn` 字体)先核实归属与可用性,不重复搬运已符合目标的文件。 + +生成可追溯映射:`原 cloud:// fileID → 新 objectKey/assetId → checksum/大小/类型 → 迁移状态`。确认源文件后下载、上传七牛并验证内容;仅改 URL 不算文件已迁移。 + +- 对可公开使用的预览图、共享图片和小程序所需字体使用公共资源空间/域名,采用版本化对象 key 和缓存。 +- 头像等用户文件使用私有访问策略;数据库存对象引用,API 生成访问地址。小程序缓存需记录有效期并刷新,不能把短期签名 URL 当永久 avatarUrl。 +- 未来私有线稿/模板另行存放;当前小程序使用的字体与最终预览不因迁移就自动变成保密素材。 +- 首期头像和预览图较小,统一通过 `POST /api/v1/assets` 上传,由 API 校验身份、用途、类型、大小和归属,再写七牛。普通用户仅上传本人头像;预览上传需要管理权限。 +- 后续有大文件需求时再加入七牛直传凭证。AK/SK 只在服务端;上传地址、对象 key 与归属由服务端控制,不接受任意远程 URL 抓取。 +- 旧版仍会先上传 CloudBase 再调用更新资料/发布接口。兼容期保留所需上传能力,兼容入口校验旧文件环境、允许路径和所有者后复制到七牛再提交新数据库;不把永久 cloud:// 引用写入新数据库。 + +新版本完成后,正式代码和兜底资源均不再依赖 cloud://。旧版本硬编码字体/预览无法通过接口转发改掉,所以旧文件需要与兼容期一起保留,退场时单独检查访问量。 + +### 7.2 首页配置与推荐 + +现有 `app.ts` 顺序为预拉取 → 本地缓存 → 云函数兜底。迁移后由新公共 `GET /api/v1/page-config` 提供配置,微信后台预拉取 URL 同步更新,失败继续使用可用缓存/内置数据,网络恢复后按版本刷新。 + +- 新 API 保留预拉取解析所需的数据形状与 `previewImg`;升级缓存命名/版本,避免长期沿用 cloud:// 和旧首页字段。 +- PostgreSQL 中的 current 配置是权威源。静态配置文件若为缓存或旧版兼容发布,只是派生产物;发布失败可重试,保留上一有效版本,不形成第二套内容数据库。 +- 手动栏目设置、各 page 分支及 category/age 为 null 的现有行为通过固定输入样本比对,避免迁移时无意改变运营配置。 +- 自动刷新仍只更新下载/收藏 Top5,保留手动维护的其他栏目。分龄推荐保持现有“ageMin 等于目标下限且 ageMax 覆盖上限”的匹配方式,业务算法调整留到后续。 +- 排名并列时定义稳定排序并与旧样本核对。统计更新不等于内容上新,保留 createdAt/contentUpdatedAt/updatedAt 各自用途。 + +### 7.3 定时任务 + +仓库存在 7 段定时表达式 `0 0 22 * * * *`,不得直接当作新调度器的表达式或凭它认定北京时间执行时刻。先核对 CloudBase 控制台实际时区与最近执行记录,再换为服务端明确时区的计划。 + +保留 enabled 开关;首次配置缺失默认启用的规则显式记录。自动执行与手动执行共用内容服务,使用锁/事务避免互相覆盖,记录执行时间、结果与失败原因。服务重启后发现当日应执行任务遗漏,可补跑一次;旧任务在正式切换前停用。 + +## 8. 小程序接入与旧版兼容 + +### 8.1 新版改造 + +- 本节改造在独立的 `doodle-mini` 项目中实施,不移动小程序源码到 `doodle-store`;按新项目公开 API 契约接入,记录联调使用的服务端版本。 +- 按开发、体验、正式配置 API 地址,不在生产构建中使用 localhost/IP。接口域名、下载资源域名及实际上传域名按微信后台要求配置 HTTPS 与备案。 +- 建立业务服务层(auth/worksheet/category/content/favorite/download/asset/admin),页面调用业务方法。当前散落的调用点逐一替换,不依赖修改单个 cloud-adapter 就完成迁移。 +- HTTP 包装补齐登录、错误格式、超时和日志;`wx.uploadFile`、`wx.downloadFile` 分别处理。当前 `utils/http.ts` 下载包装读取 `res.data` 不适用于 `wx.downloadFile` 的临时文件路径,使用前修正契约。 +- 所有正式入口及分享 path 保留。收藏/下载历史中的原 worksheetId 能打开原题型,不因存储变化重定向到默认题型。 +- 网络故障时保留已有生成/保存能力;远程收藏与历史展示明确失败/缓存状态,不将失败误显示为真实空数据。 +- 微信事件名和 worksheet_id/export_id 口径延续;收藏成功事件仍在远端成功后记录。 + +### 8.2 过渡期兼容桥 + +发布新版并不意味着所有旧版立即消失。兼容桥覆盖旧正式用户的登录、资料、收藏、历史和内容查询;旧管理页存在数据库直读,要求管理员使用新版,不承诺旧调试管理页完全兼容。 + +1. 原云函数名称保留,按旧 action 白名单转发统一服务端业务方法,并转换回原响应格式。 +2. 身份从 `cloud.getWXContext()` 取得,不能从 event 信任 OpenID/角色。 +3. 兼容请求以服务间密钥签名,包含函数/action、可信 AppID/OpenID、请求体、时间和唯一请求 ID;服务端验签、校验时效并拒绝重放。 +4. 内部转发重试使用新的传输请求 ID,但复用同一业务幂等键。签名密钥不下发小程序;管理 action 仍需验证管理员身份。 +5. 切换后兼容桥只读写新数据库,旧数据库停止写入;旧存储仅用于兼容文件读取/上传与迁移。 + +这一阶段有两个接入协议,但只有一份业务实现和一个写入源。兼容模块带退场期限和调用统计,不长期维护两套后端。 + +## 9. 实施里程碑与可执行待办 + +| 阶段 | 工作与交付 | 完成条件 | +| ------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| S0 盘点 | 线上集合/文件清单、资源和域名核实、固定功能样本、迁移报告模板 | 迁移对象、数量和异常可追溯;有原始备份 | +| S1 纵向样板 | doodle-store pnpm 工作区、apps/server 骨架、数据库迁移、微信登录、题型读取、收藏/下载事务 | 工作区安装/构建通过;老用户身份关联正确;重复收藏/保存请求不重复计数 | +| S2 功能迁移 | 补齐其余 API、管理权限、配置/推荐/任务、文件模块 | 17 个云函数各有落点,管理页直读替代完成 | +| S3 接入与试迁 | doodle-mini 服务层改造、跨项目契约核对、文件与数据库试导、兼容桥、体验版部署 | 新旧用户协议共同访问测试数据库;页面/字体/预拉取实测通过 | +| S4 上线切换 | 生产导入、单写切换、正式版本发布、在线核对 | 用户关联与累计数据一致;新写入只在新数据库 | +| S5 退场 | 持续核对兼容/旧文件调用、移除云开发依赖、归档旧数据 | 达到退场门槛后完全退出 CloudBase | + +### 执行清单 + +- [ ] S0:导出实际集合及文件清单;确认 API 域名、备案、服务器和七牛环境。 +- [ ] S0:记录老用户/题型/推荐/配置样本及当前原始计数,准备数据审计规则。 +- [x] S1:新建独立 `doodle-store` 项目,建立 pnpm workspace、锁文件、版本约束及 `apps/server` 的独立构建/检查脚本;不迁入 `doodle-mini` 源码。2026-10-12 已完成基础初始化,格式/代码/类型检查、5 项基础测试与构建入口 HTTP 检查通过;不代表 S1 业务样板已完成。 +- [ ] S1:在 `doodle-store/apps/server` 建立 PostgreSQL/Prisma 数据结构、统一响应、日志与版本化公开接口契约。 +- [ ] S1:微信登录沿用原 userId;服务端权限校验;收藏与下载幂等事务。 +- [ ] S2:完成全部公开/用户/管理接口及现有推荐行为对照。 +- [ ] S2:迁移文件、首页预拉取配置和定时任务,完成手动/自动刷新互斥。 +- [ ] S3:在 `doodle-mini` 替换所有正式云调用、管理直读、上传和字体解析,核对旧缓存升级及新 API 契约;记录两个项目的联调版本。 +- [ ] S3:验证兼容签名、身份、响应格式、旧文件新增迁移及无 exportId 请求行为。 +- [ ] S3:试导后核对集合总量、主键、关联、转换/隔离项及文件校验。 +- [ ] S3:完成备份恢复演练、API 检查和微信体验版真机验收。 +- [ ] S4:准备写入冻结、切换顺序、回滚记录,再执行生产切换与正式发布。 +- [ ] S5:确认兼容/旧资源访问退场门槛,归档后停止旧任务、函数、存储与套餐。 + +## 10. 正式切换与回滚 + +### 10.1 推荐切换:短维护窗口,全量最终导入 + +现有业务较小,优先使用经过演练的短写入冻结,不建设长期双写或增量同步平台。窗口时长由试导耗时决定,不能提前承诺“零停机”或固定分钟数。 + +1. 先完成试迁和体验版验收,保持现有正式版仍使用 CloudBase。新正式版完成审核准备,但暂不放量。 +2. 预部署全部兼容函数,支持统一的 `cloud / readonly / server` 切换模式;在演练中验证。旧云函数对用户创建/活跃时间、收藏、下载、资料和管理写入均受模式约束。 +3. 选择低峰窗口,冻结旧端全部业务写入,停止旧推荐任务和管理发布;明确提示维护,读取与本地生成/保存仍可用。窗口内旧版成功保存但日志未上报的限制应在切换记录中注明;新版补报队列在试迁时验证。 +4. 在冻结状态导出最终快照,完成新增文件迁移和全量导入审计;此前试迁目标不得保留混入的测试记录。 +5. 将兼容桥整体切到 server,新数据库成为唯一写入源;更新公共配置/预拉取来源,开启新定时任务。核对桥和任务状态,旧数据库保持只读。 +6. 发布新版小程序;过渡期新版 HTTP 和旧版桥均调用同一业务服务。新版本未推广完成也不得把一部分用户写回旧数据库。 +7. 核对登录、收藏、成功保存记录、头像/字体、首页刷新与真实计数,再进入观察期。 + +若首次盘点发现数据规模或维护窗口不满足全量导入,应重新设计最终同步方式,不能直接在生产并行写两个库。试迁阶段的测试用户和数据不得混入生产。 + +### 10.2 回滚的两种情况 + +| 时点 | 回滚方式 | +| ------------------------ | ------------------------------------------------------------- | +| 新库尚未接收真实业务写入 | 停新任务/新入口,将桥恢复 cloud;解除旧库写入冻结,恢复旧任务 | +| 新库已接收真实业务写入 | 优先回滚 API 版本并继续使用新数据库;不可直接把客户端切回旧库 | + +如必须回到旧数据库:同时冻结 HTTP 和兼容桥写入,导出新库最新数据并反向转换,将切换后的新增、修改和删除全部同步到旧库,完成关联/计数审计后再恢复旧入口。反向导入按新库权威快照替换相关数据,不能只补新增,否则会复活已取消收藏或已注销用户。 + +S3 应验证反向转换和恢复步骤。每次切换保存版本、模式、时间点和审计报告;数据库变更采用可兼容旧 API 版本的方式,避免应用回滚时表结构无法使用。 + +### 10.3 CloudBase 退场门槛 + +建议先观察至少 14 天,再结合实际旧版调用决定退场;14 天本身不是可以删除云资源的证明。要求: + +- 新版正式功能及管理操作已稳定使用新 API。 +- 兼容调用和旧 cloud:// 文件访问在约定观察窗口内已归零,或明确接受剩余旧版必须升级才能继续在线使用;不能凭新版发布推断旧版本消失。 +- 新数据库、七牛文件及恢复副本均可使用,旧数据已完成可恢复归档。 +- 旧任务停止,新版无 CloudBase 运行依赖;客户端 cloud 初始化及项目云配置在最终清理阶段移除。 + +删除旧资源和停止套餐应在上述条件满足后执行。兼容函数、上传或云字体仍使用旧环境时,不能宣称已完全迁出或已省下全部 19.9 元费用。 + +## 11. 验收与最低运行要求 + +### 11.1 自动验证与真实环境验收 + +| 验证层 | 检查项目 | 通过标准 | +| ----------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------- | +| 服务端自动检查 | 登录错误、用户越权、管理权限、分页、重复收藏、并发重复 exportId、事务失败、注销 | 不越权、不重复计数;失败无半条业务写入 | +| 工程与契约检查 | doodle-store 工作区安装/构建、应用依赖边界、公开契约与 doodle-mini 适配 | 服务端可独立构建;客户端不导入私有模块;联调版本及兼容行为可追溯 | +| 行为对照 | 固定输入的分类、分龄、手动配置、自动榜单、展示/真实计数 | 字段、去重、排序、兜底与预期一致;差异显式记录 | +| 迁移审计 | ID、数量、日期、关系、重复/隔离项、旧计数、文件 checksum | 数据量可对账;可用关联完整;无未解释丢失 | +| 小程序体验版/真机 | 老用户登录、首页/分类/年龄、预览生成、实际保存、收藏/历史、资料、注销、管理发布 | 使用有效域名和证书;功能链路与原版一致 | +| 故障与回滚 | token 过期、离线、保存后上报失败、API 重启、备份恢复、切换后新增/删除 | 本地保存结果准确;同事件重试不重复;恢复不遗漏切换后的变化 | + +微信开发者工具“跳过域名校验”下通过不算正式网络验收。至少检查 iOS、Android 的上传、图片/字体加载和保存回调。服务端和脚本可以自动测试;相册权限、实际保存、微信分享、广告、预拉取与真机字体效果需实际微信环境验证,不能以静态检查替代。 + +管理员验收包括:首页手动推荐/栏目、定时开关、分类创建/更新、题型上传/发布/上下架、年龄标签同步及 dryRun。正式用户验收还应覆盖所有分类入口、首页推荐、分龄和收藏历史中的跳转路径。 + +### 11.2 轻量运行基线 + +- 每日自动备份数据库到服务器之外,备份访问受控并保留滚动版本;上线前成功恢复一次。七牛文件迁移清单和必要配置随数据备份保留。 +- 初始目标可设数据库 RPO 不超过 24 小时、恢复操作 RTO 不超过 60 分钟,以演练结果确认;若需要更小数据损失窗口,再增加 binlog/更频繁备份。每日备份不等于零数据损失。 +- HTTPS 自动续期;检查 API/数据库健康、磁盘、错误、备份失败和定时任务失败。日志轮转,不记录 AppSecret、登录 code、令牌、签名 URL 或完整用户敏感信息。 +- 版本和数据库变更可追踪;更新前备份,保留上一可运行应用版本。对本项目采用简单可执行步骤,不要求集群或复杂运维平台。 +- 发布记录关联 `doodle-store` 服务端版本、数据库迁移版本和 `doodle-mini` 小程序版本;后续后台/Web 各自记录版本。回滚服务端时保持已发布客户端的 API 兼容,不要求两个项目同步回滚。 +- 成本按 API 流量、数据库体量、七牛存储/下载/请求和实际维护工时记录,不以“已有服务器”推导零总成本。 + +## 12. AI 辅助工作量与排期 + +以下是初步预算,不是已验证的工期承诺。假设线上数据和文件较少、服务器可部署、权限齐备、沿用本方案接口/数据库选择;包含 `doodle-store` 基础 pnpm 工作区与 `apps/server`,以及 `doodle-mini` 接入改造,不包含独立后台/Web 页面、私有包发布平台或存量绘图重写。 + +| 工作包 | AI 辅助后的初步投入 | +| -------------------------------------------------------------- | ------------------------------------------------ | +| 实际盘点、固定样本、接口与导入字段确认 | 2–3 小时 | +| doodle-store 工作区、服务端骨架、登录/权限、现有业务与管理 API | 8–12 小时 | +| 数据/文件导入、对账与反向转换脚本 | 4–8 小时 | +| 小程序服务层、登录、上传、缓存/字体与页面适配 | 6–10 小时 | +| 部署、兼容桥、必要测试/真机联调与切换演练 | 6–10 小时 | +| 合计 | 26–43 小时;约加 25% 修正余量后按 32–54 小时准备 | + +按每天 8 小时集中投入,可先预算约 4–7 个工作日;兼职开发的日历周期更长,外部审核/备案等待另算。S5 还有至少 14 天的退场观察,代码与正式切换完成时间不等于关闭 CloudBase 的时间,观察期也不代表连续全日投入。先完成 S0 + S1 的纵向样板,以实际用时、数据量和发现的问题更新后续预算。 + +AI 主要承担接口/模型初稿、SQL 转换、数据脚本、小程序调用替换、部署配置和测试用例;必须运行验证的内容是老用户身份、导入结果、文件可用性、实际保存、管理权限及生产切换。无需将常规备份和升级夸大为单独的大型工程。 + +## 13. 与现有规划的关系及参考 + +本方案覆盖旧文档“暂缓核心 API/数据库迁移”“只替换 cloud-adapter 即可”的表述。目标改为自有服务器统一后端;过渡期保留 CloudBase 仅用于兼容和恢复。现有产品功能待办、Web 试点和服务端绘图的范围分别管理。 + +工程组织以本方案为准:不在 `doodle-mini` 中新增服务端工程;独立 `doodle-store` pnpm Monorepo 管理服务端、后续后台和 Web,目前只完成基础初始化,业务迁移仍待实施。本方案迁入 `doodle-store/docs/云服务端迁移方案.md`,不在两个项目中维护重复副本。 + +实施依据是 `doodle-mini` 当前代码,历史文档中的 Prisma 示例、免费额度、Node 版本和“零成本 CDN”表述不能直接作为部署事实。 + +- [微信小程序网络与服务器域名要求](https://developers.weixin.qq.com/miniprogram/dev/framework/ability/network.html):HTTPS、备案、request/upload/download 域名与真实环境校验。 +- [微信小程序登录流程](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html):code2Session 与身份/会话密钥边界。 +- [七牛上传凭证](https://developer.qiniu.com/kodo/1208/upload-token):凭证由服务端生成,限制范围和有效期;直传作为后续可选优化。 +- [PostgreSQL 数据库设计(v3.5)](./v3.5/数据库设计文档.md):表结构、约束、事务、导入映射与数据库验收;具体 PostgreSQL / Prisma 版本在实施时验证并锁定。 +- [pnpm 工作区](https://pnpm.io/workspaces):Monorepo、内部包引用及工作区锁文件。 +- [pnpm 筛选命令](https://pnpm.io/filtering):按应用、依赖关系执行开发与检查任务。 +- `doodle-mini` 仓库依据:`cloudfunctions/*/index.js`、`miniprogram/app.ts`、`utils/auth.ts`、`favorites.ts`、`downloadLogs.ts`、`downloadPrint.ts`、`base/pageMixin.ts`、`pages/profile/profile.ts`、`core/font/*` 及上述管理页面。 diff --git a/docs/初始化说明.md b/docs/初始化说明.md new file mode 100644 index 0000000..3dadee1 --- /dev/null +++ b/docs/初始化说明.md @@ -0,0 +1,43 @@ +# Doodle Store 初始化说明 + +初始化日期:2026-10-12。 + +## 工程边界 + +- 新项目路径:`/Users/joey-xd/sites/doodle/doodle-store`。 +- 小程序继续在 `/Users/joey-xd/sites/doodle/doodle-mini` 独立维护,未搬移源码或更改其依赖。 +- 本项目采用 pnpm workspace;首期应用是 `apps/server`,后续加入 `apps/admin` 和 `apps/web`。 +- 内部共享包使用 `workspace:*`;跨项目使用有版本的 HTTPS API,不直接引用彼此源码。 + +## 已初始化的能力 + +- 固定 Node.js 24.20.0、pnpm 11.24.0,使用单一锁文件。 +- NestJS 11 / TypeScript 5.9 服务端骨架;选择兼容版本,不直接追随依赖的最新主版本。 +- 严格类型检查、ESLint、Prettier 和按依赖顺序执行的构建脚本。 +- 可选 `.env` 加载、端口/监听地址校验、关闭信号处理。 +- `GET /api/v1/health` 进程存活检查及真实 HTTP 测试。 +- admin、web、Prisma、迁移脚本和部署目录的用途说明。 + +## 初始化验证 + +- `pnpm install --frozen-lockfile --offline`:锁文件重复安装通过。 +- `pnpm check`:格式、代码规范、类型检查、构建与 5 项基础测试通过。 +- 使用已构建的 `apps/server/dist/main.js` 启动真实进程,本地请求 `/api/v1/health` 返回 HTTP 200 和预期响应;验证后关闭进程。 +- 已初始化独立本地 Git 仓库,尚未提交、配置远程仓库或推送。 + +## 未开发的内容 + +尚未实现登录/权限、用户资料、分类、题型、收藏、下载、推荐、管理业务、七牛、定时任务或私有绘图。未连接数据库、迁移数据、发布小程序或部署云服务器。 + +数据库选型讨论不等于最终变更指令;当前仅预留 Prisma 目录,不安装 ORM 或生成表结构。后续按确认后的数据库选型实施。 + +## 后续开发要求 + +1. 按迁移方案先完成登录、题型读取、收藏/下载幂等事务的纵向样板,不一次生成所有业务模块。 +2. `apps/server/prisma` 作为数据库结构唯一来源,原用户/题型字符串 ID 必须保留。 +3. 公共契约不得包含私有实现;管理权限由服务端判断,浏览器不直接访问数据库。 +4. 小程序联调记录服务端与客户端版本,保留旧版本兼容期;不能假设同一时间升级所有用户。 +5. 新增共享包后,确保根目录构建/检查先处理依赖包,再处理使用它们的应用。 +6. CI 可使用 `pnpm install --frozen-lockfile` 和 `pnpm check`;当前未连接 CI 服务或创建远程仓库。 + +业务迁移范围与切换步骤以本项目的[云服务端迁移方案](./云服务端迁移方案.md)为准;该文档已从 `doodle-mini` 移入本项目统一维护。本文件只描述初始化状态,不代表 S1 业务样板已完成。 diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..0aae96b --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,23 @@ +import js from '@eslint/js'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + ignores: [ + '**/node_modules/**', + '**/.pnpm-store/**', + '**/dist/**', + '**/dist-test/**', + '**/coverage/**', + ], + }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['apps/server/**/*.ts', '**/*.mjs'], + languageOptions: { + globals: globals.node, + }, + }, +); diff --git a/package.json b/package.json new file mode 100644 index 0000000..914a467 --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "doodle-store", + "version": "0.1.0", + "private": true, + "description": "Doodle 服务端、管理后台与 Web 工作区", + "packageManager": "pnpm@11.24.0", + "engines": { + "node": ">=24.20.0 <25", + "pnpm": "11.24.0" + }, + "scripts": { + "dev": "pnpm --filter @doodle/contracts build && pnpm --filter @doodle/server dev", + "build": "pnpm -r --if-present run build", + "start": "pnpm --filter @doodle/server start", + "typecheck": "pnpm --filter @doodle/contracts build && pnpm -r --if-present run typecheck", + "lint": "eslint . --max-warnings 0", + "format": "prettier . --write", + "format:check": "prettier . --check", + "test": "pnpm --filter @doodle/server... run build && pnpm --filter @doodle/server test", + "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test" + }, + "devDependencies": { + "@eslint/js": "10.0.1", + "eslint": "10.12.0", + "globals": "17.13.0", + "prettier": "3.9.10", + "typescript": "5.9.3", + "typescript-eslint": "8.71.1" + } +} diff --git a/packages/contracts/README.md b/packages/contracts/README.md new file mode 100644 index 0000000..ecc9598 --- /dev/null +++ b/packages/contracts/README.md @@ -0,0 +1,7 @@ +# 公共接口契约 + +仅包含可公开的基础响应和健康检查类型。服务端通过 `workspace:*` 引用,使用 `import type` 避免引入无必要的运行时逻辑。 + +后续可以加入公开请求/响应类型及枚举,但禁止包含 Prisma Client、数据库模型、密钥、管理权限实现或私有生成器。 + +独立项目 `doodle-mini` 不直接引用本地工作区包;后续通过版本化接口文档/OpenAPI 同步必要类型,并记录来源版本。 diff --git a/packages/contracts/package.json b/packages/contracts/package.json new file mode 100644 index 0000000..af51e54 --- /dev/null +++ b/packages/contracts/package.json @@ -0,0 +1,24 @@ +{ + "name": "@doodle/contracts", + "version": "0.1.0", + "private": true, + "type": "commonjs", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc --noEmit -p tsconfig.json" + }, + "devDependencies": { + "typescript": "5.9.3" + } +} diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts new file mode 100644 index 0000000..f6b4d94 --- /dev/null +++ b/packages/contracts/src/index.ts @@ -0,0 +1,12 @@ +/** 可公开的成功响应类型;不定义服务端内部错误或数据库实体。 */ +export interface ApiSuccess { + code: 0; + message: string; + data: T; +} + +/** 只描述 HTTP 进程存活,不代表外部服务已接入或健康。 */ +export interface HealthStatus { + status: 'ok'; + service: 'doodle-store-server'; +} diff --git a/packages/contracts/tsconfig.json b/packages/contracts/tsconfig.json new file mode 100644 index 0000000..8d41b64 --- /dev/null +++ b/packages/contracts/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "Node16", + "moduleResolution": "Node16", + "lib": ["ES2022"], + "types": [], + "declaration": true, + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..4743a3f --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3471 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@eslint/js': + specifier: 10.0.1 + version: 10.0.1(eslint@10.12.0(supports-color@8.1.1)) + eslint: + specifier: 10.12.0 + version: 10.12.0(supports-color@8.1.1) + globals: + specifier: 17.13.0 + version: 17.13.0 + prettier: + specifier: 3.9.10 + version: 3.9.10 + typescript: + specifier: 5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: 8.71.1 + version: 8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) + + apps/server: + dependencies: + '@doodle/contracts': + specifier: workspace:* + version: link:../../packages/contracts + '@nestjs/common': + specifier: 11.2.7 + version: 11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1) + '@nestjs/core': + specifier: 11.2.7 + version: 11.2.7(@nestjs/common@11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/platform-express@11.2.7)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/platform-express': + specifier: 11.2.7 + version: 11.2.7(@nestjs/common@11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/core@11.2.7)(supports-color@8.1.1) + reflect-metadata: + specifier: 0.2.2 + version: 0.2.2 + rxjs: + specifier: 7.8.2 + version: 7.8.2 + devDependencies: + '@nestjs/cli': + specifier: 11.0.24 + version: 11.0.24(@types/node@24.19.2)(prettier@3.9.10) + '@nestjs/testing': + specifier: 11.2.7 + version: 11.2.7(@nestjs/common@11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/core@11.2.7)(@nestjs/platform-express@11.2.7) + '@types/node': + specifier: 24.19.2 + version: 24.19.2 + typescript: + specifier: 5.9.3 + version: 5.9.3 + + packages/contracts: + devDependencies: + typescript: + specifier: 5.9.3 + version: 5.9.3 + +packages: + + '@angular-devkit/core@19.2.24': + resolution: {integrity: sha512-Kd49warf6U/EyWe5BszF/eebN3zQ3bk7tgfEljAw8q/rX95UUtriJubWvp6pgzHfzBA4jwq8f+QiNZB8eBEXPA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^4.0.0 + peerDependenciesMeta: + chokidar: + optional: true + + '@angular-devkit/core@19.2.27': + resolution: {integrity: sha512-3amNzoCVSKd7ah6l6lBQL4onwwJvqvam7FMoQBILrxtW5LB5ezh8gMSPuA4zJjKjoRzf9uoWdlzqv/84I52xZA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^4.0.0 + peerDependenciesMeta: + chokidar: + optional: true + + '@angular-devkit/schematics-cli@19.2.27': + resolution: {integrity: sha512-wHYH6SVXVykhLzovUHtYor3Nl4SpIiITi7r9DQDaKYUD4hpRBx25W6N9eGuakT9Vd5tV/x6wmvQFWQZQwFB7eA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + hasBin: true + + '@angular-devkit/schematics@19.2.24': + resolution: {integrity: sha512-lnw+ZM1Io+cJAkReC0NPDjqObL8NtKzKIkdgEEKC8CUmkhurYhedbicN8Y8NYHgG1uLd2GozW3+/QqPRZaN+Lw==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + + '@angular-devkit/schematics@19.2.27': + resolution: {integrity: sha512-/PZmyAlb2NGWPikRRuiWLdfHQd8Wrx6lX4HqvTcaDhlU43M3T0ud4PH2T3QDp7BzHYY92xtD8iPxX2asg67G1A==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@borewit/text-codec@0.2.2': + resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} + + '@cacheable/memory@2.2.0': + resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==} + + '@cacheable/utils@2.5.0': + resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==} + + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/config-helpers@0.7.0': + resolution: {integrity: sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/js@10.0.1': + resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.7.3': + resolution: {integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@inquirer/ansi@1.0.2': + resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} + engines: {node: '>=18'} + + '@inquirer/checkbox@4.3.2': + resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.21': + resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.3.2': + resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@4.2.23': + resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.23': + resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.15': + resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} + engines: {node: '>=18'} + + '@inquirer/input@4.3.1': + resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.23': + resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@4.0.23': + resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.10.1': + resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.3.2': + resolution: {integrity: sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.1.11': + resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@3.2.2': + resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@4.4.2': + resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@3.0.10': + resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + + '@lukeed/csprng@1.1.0': + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + + '@nestjs/cli@11.0.24': + resolution: {integrity: sha512-aIHxQLSYtXShifA3zwWIeznEsZnNa3Iz2QRykFj+sl9IcbERBHr5nH87FRgywM+He3NxoF5WazHfR8FsmVeWxw==} + engines: {node: '>= 20.11'} + hasBin: true + peerDependencies: + '@swc/cli': ^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0 + '@swc/core': ^1.3.62 + peerDependenciesMeta: + '@swc/cli': + optional: true + '@swc/core': + optional: true + + '@nestjs/common@11.2.7': + resolution: {integrity: sha512-CGOa0I/5Ac43ydfpjztozOrvgDWYBdY9ktkO0OrRqRo1d8n5cphtrTGYHOwKLMIel0BpSYZoQymJ9Dywv/BzLw==} + peerDependencies: + class-transformer: '>=0.4.1' + class-validator: '>=0.13.2' + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + + '@nestjs/core@11.2.7': + resolution: {integrity: sha512-ynMkVOAEykJMtls1X+MQV3TNY6hrnMkyHowU8XY8R6UqM3tdqT4WNVtemQOmFrqHw3f85lbe765fcpZyjBOcqA==} + engines: {node: '>= 20'} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/microservices': ^11.0.0 + '@nestjs/platform-express': ^11.0.0 + '@nestjs/websockets': ^11.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + + '@nestjs/platform-express@11.2.7': + resolution: {integrity: sha512-+F0hV/4yJM+D/Qwyo5XmKMe6YGEb+ssT2jFWrgdFe1obumXCUeC1GNob/kNeXyzAXm7dcxZVUtmcoqaF6z36jA==} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/core': ^11.0.0 + + '@nestjs/schematics@11.1.0': + resolution: {integrity: sha512-lVxGZ46tcdItFMoXr6vyKWlnOsm1SZm/GUqAEDvy2RL4Q4O+3bkziAhrO7Y8JLssFUUvNFEGqAizI52WAxhjDw==} + peerDependencies: + prettier: ^3.0.0 + typescript: '>=4.8.2' + peerDependenciesMeta: + prettier: + optional: true + + '@nestjs/testing@11.2.7': + resolution: {integrity: sha512-CoWN8pIjXPVhkbaT0JI1E7X/QOnf9A8cdrzuhU+9mzRS4296/kds6BT+4tH7kOH484tH3GWkkB0+K7dA/FVGKg==} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/core': ^11.0.0 + '@nestjs/microservices': ^11.0.0 + '@nestjs/platform-express': ^11.0.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + + '@tokenizer/inflate@0.4.1': + resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} + engines: {node: '>=18'} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@24.19.2': + resolution: {integrity: sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==} + + '@typescript-eslint/eslint-plugin@8.71.1': + resolution: {integrity: sha512-nNlBf6HuotqMgZvSkWD5hNdemiBIxQ2NmrlgfcUIeZRs+BbakjscCQRq/Hb74EFVnFiSWWdL12JruqwBy3ERDw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.71.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.71.1': + resolution: {integrity: sha512-oM/5sAqz/l1v/zYK1qTOPrn2+JIrBGC1V6uNYucRO5gh6eT9vWJ/RhPbGE59byvZEUQ4VP6XkLBaS5Jti5jpvw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.71.1': + resolution: {integrity: sha512-Oijc9RUsohHjncg6iGi7gCtjnEjYt9FWv9u6ygK8uKcHK5eLjLAkqERQ9BaytbpceOFdk6kifPCS/zAZE+A99g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.71.1': + resolution: {integrity: sha512-1YUDZXdTnXLlob03SFHBipdyMBsDVm7zoSz0ytTJ39dk5J1TgsTK03VlR/dLXG/RLLUrPcYbD4rp23BmHxHP0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.71.1': + resolution: {integrity: sha512-oTvkml5SxXhgg+WWKhod5qWQICDXtOB+/Hi9VdPKFAANJ2b7YOgMffgvzXuqog7R6A/JtrzRf128dsxummyWhg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.71.1': + resolution: {integrity: sha512-kH3t3jZCYv2PZSBEWPKTariLrCaoF1CLoPitYFBJOhAGEwWnSRAtGEifrfrpBa4K9JlwWNZotgA5w3/wLSk/FQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.71.1': + resolution: {integrity: sha512-ybYnPTUwg3VQQOFEiBPLs8Jxl1ry2vXdn1kVKguomBbvxpetdu3MKTNDqFNUrlG8ylP9MpZosmXMFYlGQHk1oQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.71.1': + resolution: {integrity: sha512-pSKSEK1JJpHs6KiKVtABCE4iJK5d7FLFzDXQNBQ2oRY7LEd3UwOt3TZemTgu9syTIf56dJWlTn/pdmrO9oKUXQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.71.1': + resolution: {integrity: sha512-CBjT6gfAz3DW2j9Q4moFnqt208Aq+506sVGXfpRcyAeqn0EpayL0mjdWoCO2t3RC7GewNrFxdeehuEaPg6DMzQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.71.1': + resolution: {integrity: sha512-0GxiUGqMU0qXJt58cBsiG3DP8lUmBaFo2vChtUX+CT7PnR3v/VGLLrzVP4K9r1lNiZXjHYefMBbPunplr09v6w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.19.0: + resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansis@4.2.0: + resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + engines: {node: '>=14'} + + append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.11.28: + resolution: {integrity: sha512-br0LOmjJqr6Rt4AEkadbcNf67Zpo7EPwKsWbjnRrJIa1QYFHQBqM4C7F2WTbV8ahTEcg1Eo+Y0aWjjORInYFyA==} + engines: {node: '>=6.0.0'} + hasBin: true + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@2.3.0: + resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} + engines: {node: '>=18'} + + brace-expansion@1.1.21: + resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==} + + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} + + browserslist@4.29.3: + resolution: {integrity: sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cacheable@2.5.0: + resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001815: + resolution: {integrity: sha512-uDybUEyfgbyJJ93J1vikTylBX31AkdVYeVPeffMQpf8XN7hSfr0oZOn4VFOIMNvtU5+rHvC5Udg2TgtJKHm23Q==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + comment-json@5.0.0: + resolution: {integrity: sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==} + engines: {node: '>= 6'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + content-type@2.1.0: + resolution: {integrity: sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==} + engines: {node: '>=18'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.452: + resolution: {integrity: sha512-cVvk1YBSj6571VmV6hdJTqj94Y79xSu8cj6Y9t6ae4Y4431z8/7EJFvu3rVp90xhfuv9BpPKUmaLfTo4+FOGBg==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + enhanced-resolve@5.26.0: + resolution: {integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==} + engines: {node: '>=10.13.0'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.12.0: + resolution: {integrity: sha512-npHjrHb2o4ehH3mE+YuxABR+0gyb0aVWCIolgJDArwCiHEIsonBm3ZKjel5b0U5+a+MbEvIyRCGCzD3eKKK2NA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-uri@3.1.8: + resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@11.1.5: + resolution: {integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==} + + file-type@21.3.4: + resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==} + engines: {node: '>=20'} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@6.1.23: + resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + + fork-ts-checker-webpack-plugin@9.1.0: + resolution: {integrity: sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==} + engines: {node: '>=14.21.3'} + peerDependencies: + typescript: '>3.6.0' + webpack: ^5.11.0 + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-monkey@1.1.0: + resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + + globals@17.13.0: + resolution: {integrity: sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hashery@1.5.1: + resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==} + engines: {node: '>=20'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} + + hookified@2.2.0: + resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.12: + resolution: {integrity: sha512-/8UvqAPU9DGTI9k4mxtf49U37Isfwr8Uts96+SBHIkFxPJnHS0Ew4f00sM4Scd8V8EjM0jUNtVDdv1kPdt35lg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + 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-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-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-esm@1.0.3: + resolution: {integrity: sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==} + engines: {node: '>=13.2.0'} + + loader-runner@4.3.2: + resolution: {integrity: sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==} + engines: {node: '>=6.11.5'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + lru-cache@11.5.3: + resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} + engines: {node: 20 || >=22} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + media-typer@1.1.1: + resolution: {integrity: sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==} + engines: {node: '>= 0.8'} + + memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multer@2.4.0: + resolution: {integrity: sha512-7dqa0ZcFfzbefdTuIkzOSMvZWC0J7FLqBOjJUZvDCXShIURWKxAyTT1wHhnE5q19c7jOJf43IYYKjBmZVZmvhg==} + engines: {node: '>= 10.16.0'} + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@1.1.0: + resolution: {integrity: sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==} + engines: {node: '>=18'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + + node-emoji@1.11.0: + resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} + + node-releases@2.0.58: + resolution: {integrity: sha512-BdiKAgbfRlvfMq/XzO/AF9NPmaA8a+YpLAyAFTVFBj3gARrAIfWvNq7Q7NE4se3KgFu0KkrvQMxTS0XVnsGzHg==} + engines: {node: '>=18'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + 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'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + 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'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.9.10: + resolution: {integrity: sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==} + engines: {node: '>=14'} + hasBin: true + + proxy-addr@2.0.8: + resolution: {integrity: sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==} + engines: {node: '>= 0.10'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qified@0.10.1: + resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} + engines: {node: '>=20'} + + qs@6.16.0: + resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} + engines: {node: '>=0.6'} + + range-parser@1.3.0: + resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} + engines: {node: '>= 0.6'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + 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==} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.5.0: + resolution: {integrity: sha512-zJlMCZ0cAR5p/Y4oVpRoqioDMJcGxaXRrQ/4rP4WyR84vc5z/DolXdbvXeDpTwbtocDFr2rhPqHPErDCUtz2kA==} + engines: {node: '>= 10.13.0'} + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + 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'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + 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-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strtok3@10.3.5: + resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==} + engines: {node: '>=18'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + symbol-observable@4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + terser-webpack-plugin@5.6.1: + resolution: {integrity: sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@minify-html/node': '*' + '@swc/core': '*' + '@swc/css': '*' + '@swc/html': '*' + clean-css: '*' + cssnano: '*' + csso: '*' + esbuild: '*' + html-minifier-terser: '*' + lightningcss: '*' + postcss: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@minify-html/node': + optional: true + '@swc/core': + optional: true + '@swc/css': + optional: true + '@swc/html': + optional: true + clean-css: + optional: true + cssnano: + optional: true + csso: + optional: true + esbuild: + optional: true + html-minifier-terser: + optional: true + lightningcss: + optional: true + postcss: + optional: true + uglify-js: + optional: true + + terser@5.51.2: + resolution: {integrity: sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==} + engines: {node: '>=10'} + hasBin: true + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + token-types@6.1.2: + resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} + engines: {node: '>=14.16'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths-webpack-plugin@4.2.0: + resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==} + engines: {node: '>=10.13.0'} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + type-is@2.1.0: + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + engines: {node: '>= 18'} + + typescript-eslint@8.71.1: + resolution: {integrity: sha512-oiNVPC3/NbV1FpaHU07l8O6ynqG9v4PAiW2WvTrAmZdR0f2HI/gE668ElMLCK7Xbsu5k2fK5C4fXNiq05j7/nQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + + uint8array-extras@1.6.0: + resolution: {integrity: sha512-8iAasVS4wUx0gPLjH8Xtz2PeDzTSiy8QiLGu2DT3X5GU+egFEQhpnbtkehbAwjvDcxIERmCYcysiODFmE3Ud0Q==} + engines: {node: '>=18'} + + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.3.4: + resolution: {integrity: sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + watchpack@2.5.2: + resolution: {integrity: sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==} + engines: {node: '>=10.13.0'} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + webpack-node-externals@3.0.0: + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + engines: {node: '>=6'} + + webpack-sources@3.6.0: + resolution: {integrity: sha512-EIMmPVvNI0CYXyRPp34F2Qk7W37/BZVZIofS6LAUnrNjCVahS+hO4mxwFJDjQjODTGZDtVzLYoN2+1dYkhk9qA==} + engines: {node: '>=10.13.0'} + + webpack@5.106.2: + resolution: {integrity: sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yoctocolors-cjs@2.1.3: + resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} + engines: {node: '>=18'} + +snapshots: + + '@angular-devkit/core@19.2.24(chokidar@4.0.3)': + dependencies: + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + jsonc-parser: 3.3.1 + picomatch: 4.0.4 + rxjs: 7.8.1 + source-map: 0.7.4 + optionalDependencies: + chokidar: 4.0.3 + + '@angular-devkit/core@19.2.27(chokidar@4.0.3)': + dependencies: + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + jsonc-parser: 3.3.1 + picomatch: 4.0.4 + rxjs: 7.8.1 + source-map: 0.7.4 + optionalDependencies: + chokidar: 4.0.3 + + '@angular-devkit/schematics-cli@19.2.27(@types/node@24.19.2)(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 19.2.27(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.27(chokidar@4.0.3) + '@inquirer/prompts': 7.3.2(@types/node@24.19.2) + ansi-colors: 4.1.3 + symbol-observable: 4.0.0 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - '@types/node' + - chokidar + + '@angular-devkit/schematics@19.2.24(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 19.2.24(chokidar@4.0.3) + jsonc-parser: 3.3.1 + magic-string: 0.30.17 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + + '@angular-devkit/schematics@19.2.27(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 19.2.27(chokidar@4.0.3) + jsonc-parser: 3.3.1 + magic-string: 0.30.17 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-validator-identifier@7.29.7': {} + + '@borewit/text-codec@0.2.2': {} + + '@cacheable/memory@2.2.0': + dependencies: + '@cacheable/utils': 2.5.0 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/utils@2.5.0': + dependencies: + hashery: 1.5.1 + keyv: 5.6.0 + + '@colors/colors@1.5.0': + optional: true + + '@eslint-community/eslint-utils@4.10.1(eslint@10.12.0(supports-color@8.1.1))': + dependencies: + eslint: 10.12.0(supports-color@8.1.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.5(supports-color@8.1.1)': + dependencies: + '@eslint/object-schema': 3.0.5 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 10.2.6 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.7.0': + dependencies: + '@eslint/core': 1.2.1 + + '@eslint/core@1.2.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/js@10.0.1(eslint@10.12.0(supports-color@8.1.1))': + optionalDependencies: + eslint: 10.12.0(supports-color@8.1.1) + + '@eslint/object-schema@3.0.5': {} + + '@eslint/plugin-kit@0.7.3': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@inquirer/ansi@1.0.2': {} + + '@inquirer/checkbox@4.3.2(@types/node@24.19.2)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@24.19.2) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@24.19.2) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/confirm@5.1.21(@types/node@24.19.2)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.19.2) + '@inquirer/type': 3.0.10(@types/node@24.19.2) + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/core@10.3.2(@types/node@24.19.2)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@24.19.2) + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/editor@4.2.23(@types/node@24.19.2)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.19.2) + '@inquirer/external-editor': 1.0.3(@types/node@24.19.2) + '@inquirer/type': 3.0.10(@types/node@24.19.2) + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/expand@4.0.23(@types/node@24.19.2)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.19.2) + '@inquirer/type': 3.0.10(@types/node@24.19.2) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/external-editor@1.0.3(@types/node@24.19.2)': + dependencies: + chardet: 2.2.0 + iconv-lite: 0.7.3 + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/figures@1.0.15': {} + + '@inquirer/input@4.3.1(@types/node@24.19.2)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.19.2) + '@inquirer/type': 3.0.10(@types/node@24.19.2) + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/number@3.0.23(@types/node@24.19.2)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.19.2) + '@inquirer/type': 3.0.10(@types/node@24.19.2) + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/password@4.0.23(@types/node@24.19.2)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@24.19.2) + '@inquirer/type': 3.0.10(@types/node@24.19.2) + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/prompts@7.10.1(@types/node@24.19.2)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@24.19.2) + '@inquirer/confirm': 5.1.21(@types/node@24.19.2) + '@inquirer/editor': 4.2.23(@types/node@24.19.2) + '@inquirer/expand': 4.0.23(@types/node@24.19.2) + '@inquirer/input': 4.3.1(@types/node@24.19.2) + '@inquirer/number': 3.0.23(@types/node@24.19.2) + '@inquirer/password': 4.0.23(@types/node@24.19.2) + '@inquirer/rawlist': 4.1.11(@types/node@24.19.2) + '@inquirer/search': 3.2.2(@types/node@24.19.2) + '@inquirer/select': 4.4.2(@types/node@24.19.2) + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/prompts@7.3.2(@types/node@24.19.2)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@24.19.2) + '@inquirer/confirm': 5.1.21(@types/node@24.19.2) + '@inquirer/editor': 4.2.23(@types/node@24.19.2) + '@inquirer/expand': 4.0.23(@types/node@24.19.2) + '@inquirer/input': 4.3.1(@types/node@24.19.2) + '@inquirer/number': 3.0.23(@types/node@24.19.2) + '@inquirer/password': 4.0.23(@types/node@24.19.2) + '@inquirer/rawlist': 4.1.11(@types/node@24.19.2) + '@inquirer/search': 3.2.2(@types/node@24.19.2) + '@inquirer/select': 4.4.2(@types/node@24.19.2) + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/rawlist@4.1.11(@types/node@24.19.2)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.19.2) + '@inquirer/type': 3.0.10(@types/node@24.19.2) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/search@3.2.2(@types/node@24.19.2)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.19.2) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@24.19.2) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/select@4.4.2(@types/node@24.19.2)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@24.19.2) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@24.19.2) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.19.2 + + '@inquirer/type@3.0.10(@types/node@24.19.2)': + optionalDependencies: + '@types/node': 24.19.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@keyv/bigmap@1.3.1(keyv@5.6.0)': + dependencies: + hashery: 1.5.1 + hookified: 1.15.1 + keyv: 5.6.0 + + '@keyv/serialize@1.1.1': {} + + '@lukeed/csprng@1.1.0': {} + + '@nestjs/cli@11.0.24(@types/node@24.19.2)(prettier@3.9.10)': + dependencies: + '@angular-devkit/core': 19.2.27(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.27(chokidar@4.0.3) + '@angular-devkit/schematics-cli': 19.2.27(@types/node@24.19.2)(chokidar@4.0.3) + '@inquirer/prompts': 7.10.1(@types/node@24.19.2) + '@nestjs/schematics': 11.1.0(chokidar@4.0.3)(prettier@3.9.10)(typescript@5.9.3) + ansis: 4.2.0 + chokidar: 4.0.3 + cli-table3: 0.6.5 + commander: 4.1.1 + fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.106.2) + glob: 13.0.6 + node-emoji: 1.11.0 + ora: 5.4.1 + tsconfig-paths: 4.2.0 + tsconfig-paths-webpack-plugin: 4.2.0 + typescript: 5.9.3 + webpack: 5.106.2 + webpack-node-externals: 3.0.0 + transitivePeerDependencies: + - '@minify-html/node' + - '@swc/css' + - '@swc/html' + - '@types/node' + - clean-css + - cssnano + - csso + - esbuild + - html-minifier-terser + - lightningcss + - postcss + - prettier + - uglify-js + - webpack-cli + + '@nestjs/common@11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1)': + dependencies: + file-type: 21.3.4(supports-color@8.1.1) + iterare: 1.2.1 + load-esm: 1.0.3 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + transitivePeerDependencies: + - supports-color + + '@nestjs/core@11.2.7(@nestjs/common@11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/platform-express@11.2.7)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + dependencies: + '@nestjs/common': 11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1) + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 8.4.2 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + optionalDependencies: + '@nestjs/platform-express': 11.2.7(@nestjs/common@11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/core@11.2.7)(supports-color@8.1.1) + + '@nestjs/platform-express@11.2.7(@nestjs/common@11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/core@11.2.7)(supports-color@8.1.1)': + dependencies: + '@nestjs/common': 11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1) + '@nestjs/core': 11.2.7(@nestjs/common@11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/platform-express@11.2.7)(reflect-metadata@0.2.2)(rxjs@7.8.2) + cors: 2.8.6 + express: 5.2.1(supports-color@8.1.1) + multer: 2.4.0 + path-to-regexp: 8.4.2 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@nestjs/schematics@11.1.0(chokidar@4.0.3)(prettier@3.9.10)(typescript@5.9.3)': + dependencies: + '@angular-devkit/core': 19.2.24(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.24(chokidar@4.0.3) + comment-json: 5.0.0 + jsonc-parser: 3.3.1 + pluralize: 8.0.0 + typescript: 5.9.3 + optionalDependencies: + prettier: 3.9.10 + transitivePeerDependencies: + - chokidar + + '@nestjs/testing@11.2.7(@nestjs/common@11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/core@11.2.7)(@nestjs/platform-express@11.2.7)': + dependencies: + '@nestjs/common': 11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1) + '@nestjs/core': 11.2.7(@nestjs/common@11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/platform-express@11.2.7)(reflect-metadata@0.2.2)(rxjs@7.8.2) + tslib: 2.8.1 + optionalDependencies: + '@nestjs/platform-express': 11.2.7(@nestjs/common@11.2.7(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@8.1.1))(@nestjs/core@11.2.7)(supports-color@8.1.1) + + '@tokenizer/inflate@0.4.1(supports-color@8.1.1)': + dependencies: + debug: 4.4.3(supports-color@8.1.1) + token-types: 6.1.2 + transitivePeerDependencies: + - supports-color + + '@tokenizer/token@0.3.0': {} + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.9 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.9 + '@types/json-schema': 7.0.15 + + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@24.19.2': + dependencies: + undici-types: 7.24.6 + + '@typescript-eslint/eslint-plugin@8.71.1(@typescript-eslint/parser@8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3))(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.71.1 + '@typescript-eslint/type-utils': 8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.71.1 + eslint: 10.12.0(supports-color@8.1.1) + ignore: 7.0.12 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.71.1 + '@typescript-eslint/types': 8.71.1 + '@typescript-eslint/typescript-estree': 8.71.1(supports-color@8.1.1)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.71.1 + debug: 4.4.3(supports-color@8.1.1) + eslint: 10.12.0(supports-color@8.1.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.71.1(supports-color@8.1.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.71.1(typescript@5.9.3) + '@typescript-eslint/types': 8.71.1 + debug: 4.4.3(supports-color@8.1.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.71.1': + dependencies: + '@typescript-eslint/types': 8.71.1 + '@typescript-eslint/visitor-keys': 8.71.1 + + '@typescript-eslint/tsconfig-utils@8.71.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.71.1 + '@typescript-eslint/typescript-estree': 8.71.1(supports-color@8.1.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) + debug: 4.4.3(supports-color@8.1.1) + eslint: 10.12.0(supports-color@8.1.1) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.71.1': {} + + '@typescript-eslint/typescript-estree@8.71.1(supports-color@8.1.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.71.1(supports-color@8.1.1)(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.71.1(typescript@5.9.3) + '@typescript-eslint/types': 8.71.1 + '@typescript-eslint/visitor-keys': 8.71.1 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(supports-color@8.1.1)) + '@typescript-eslint/scope-manager': 8.71.1 + '@typescript-eslint/types': 8.71.1 + '@typescript-eslint/typescript-estree': 8.71.1(supports-color@8.1.1)(typescript@5.9.3) + eslint: 10.12.0(supports-color@8.1.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.71.1': + dependencies: + '@typescript-eslint/types': 8.71.1 + eslint-visitor-keys: 5.0.1 + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.1.0 + + acorn-import-phases@1.0.4(acorn@8.19.0): + dependencies: + acorn: 8.19.0 + + acorn-jsx@5.3.2(acorn@8.19.0): + dependencies: + acorn: 8.19.0 + + acorn@8.19.0: {} + + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-keywords@3.5.2(ajv@6.15.0): + dependencies: + ajv: 6.15.0 + + ajv-keywords@5.1.0(ajv@8.20.0): + dependencies: + ajv: 8.20.0 + fast-deep-equal: 3.1.3 + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.8 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.8 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-colors@4.1.3: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansis@4.2.0: {} + + append-field@1.0.0: {} + + argparse@2.0.1: {} + + array-timsort@1.0.3: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.11.28: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + body-parser@2.3.0(supports-color@8.1.1): + dependencies: + bytes: 3.1.2 + content-type: 2.1.0 + debug: 4.4.3(supports-color@8.1.1) + http-errors: 2.0.1 + iconv-lite: 0.7.3 + on-finished: 2.4.1 + qs: 6.16.0 + raw-body: 3.0.2 + type-is: 2.1.0 + transitivePeerDependencies: + - supports-color + + brace-expansion@1.1.21: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.12: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.29.3: + dependencies: + baseline-browser-mapping: 2.11.28 + caniuse-lite: 1.0.30001815 + electron-to-chromium: 1.5.452 + node-releases: 2.0.58 + update-browserslist-db: 1.3.4(browserslist@4.29.3) + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + + bytes@3.1.2: {} + + cacheable@2.5.0: + dependencies: + '@cacheable/memory': 2.2.0 + '@cacheable/utils': 2.5.0 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.10.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001815: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chardet@2.2.0: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chrome-trace-event@1.0.4: {} + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-spinners@2.9.2: {} + + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + + cli-width@4.1.0: {} + + clone@1.0.4: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@2.20.3: {} + + commander@4.1.1: {} + + comment-json@5.0.0: + dependencies: + array-timsort: 1.0.3 + esprima: 4.0.1 + + concat-map@0.0.1: {} + + content-disposition@1.1.0: {} + + content-type@1.0.5: {} + + content-type@2.1.0: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@8.3.6(typescript@5.9.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.3.2 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.3 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.3(supports-color@8.1.1): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + depd@2.0.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.452: {} + + emoji-regex@8.0.0: {} + + encodeurl@2.0.0: {} + + enhanced-resolve@5.26.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.3.2: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: {} + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.12.0(supports-color@8.1.1): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(supports-color@8.1.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.5(supports-color@8.1.1) + '@eslint/config-helpers': 0.7.0 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.3 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + escape-string-regexp: 4.0.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 11.1.5 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.6 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@11.2.0: + dependencies: + acorn: 8.19.0 + acorn-jsx: 5.3.2(acorn@8.19.0) + eslint-visitor-keys: 5.0.1 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + events@3.3.0: {} + + express@5.2.1(supports-color@8.1.1): + dependencies: + accepts: 2.0.0 + body-parser: 2.3.0(supports-color@8.1.1) + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3(supports-color@8.1.1) + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1(supports-color@8.1.1) + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.8 + qs: 6.16.0 + range-parser: 1.3.0 + router: 2.2.0(supports-color@8.1.1) + send: 1.2.1(supports-color@8.1.1) + serve-static: 2.2.1(supports-color@8.1.1) + statuses: 2.0.2 + type-is: 2.1.0 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-safe-stringify@2.1.1: {} + + fast-uri@3.1.8: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + file-entry-cache@11.1.5: + dependencies: + flat-cache: 6.1.23 + + file-type@21.3.4(supports-color@8.1.1): + dependencies: + '@tokenizer/inflate': 0.4.1(supports-color@8.1.1) + strtok3: 10.3.5 + token-types: 6.1.2 + uint8array-extras: 1.6.0 + transitivePeerDependencies: + - supports-color + + finalhandler@2.1.1(supports-color@8.1.1): + dependencies: + debug: 4.4.3(supports-color@8.1.1) + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@6.1.23: + dependencies: + cacheable: 2.5.0 + flatted: 3.4.4 + hookified: 1.15.1 + + flatted@3.4.4: {} + + fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.106.2): + dependencies: + '@babel/code-frame': 7.29.7 + chalk: 4.1.2 + chokidar: 4.0.3 + cosmiconfig: 8.3.6(typescript@5.9.3) + deepmerge: 4.3.1 + fs-extra: 10.1.0 + memfs: 3.5.3 + minimatch: 3.1.5 + node-abort-controller: 3.1.1 + schema-utils: 3.3.0 + semver: 7.8.5 + tapable: 2.3.3 + typescript: 5.9.3 + webpack: 5.106.2 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fs-monkey@1.1.0: {} + + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + glob@13.0.6: + dependencies: + minimatch: 10.2.6 + minipass: 7.1.3 + path-scurry: 2.0.2 + + globals@17.13.0: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + hashery@1.5.1: + dependencies: + hookified: 1.15.1 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hookified@1.15.1: {} + + hookified@2.2.0: {} + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + iconv-lite@0.7.3: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.12: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + is-arrayish@0.2.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-interactive@1.0.0: {} + + is-promise@4.0.0: {} + + is-unicode-supported@0.1.0: {} + + isexe@2.0.0: {} + + iterare@1.2.1: {} + + jest-worker@27.5.1: + dependencies: + '@types/node': 24.19.2 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + js-tokens@4.0.0: {} + + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsonc-parser@3.3.1: {} + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + keyv@5.6.0: + dependencies: + '@keyv/serialize': 1.1.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lines-and-columns@1.2.4: {} + + load-esm@1.0.3: {} + + loader-runner@4.3.2: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash@4.18.1: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + lru-cache@11.5.3: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + math-intrinsics@1.1.0: {} + + media-typer@0.3.0: {} + + media-typer@1.1.1: {} + + memfs@3.5.3: + dependencies: + fs-monkey: 1.1.0 + + merge-descriptors@2.0.0: {} + + merge-stream@2.0.0: {} + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mimic-fn@2.1.0: {} + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.12 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.21 + + minimist@1.2.8: {} + + minipass@7.1.3: {} + + ms@2.1.3: {} + + multer@2.4.0: + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + type-is: 1.6.18 + + mute-stream@2.0.0: {} + + natural-compare@1.4.0: {} + + negotiator@1.1.0: + dependencies: + content-type: 2.1.0 + + neo-async@2.6.2: {} + + node-abort-controller@3.1.1: {} + + node-emoji@1.11.0: + dependencies: + lodash: 4.18.1 + + node-releases@2.0.58: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + 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 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.3 + minipass: 7.1.3 + + path-to-regexp@8.4.2: {} + + path-type@4.0.0: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + picomatch@4.0.7: {} + + pluralize@8.0.0: {} + + prelude-ls@1.2.1: {} + + prettier@3.9.10: {} + + proxy-addr@2.0.8: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + punycode@2.3.1: {} + + qified@0.10.1: + dependencies: + hookified: 2.2.0 + + qs@6.16.0: + dependencies: + es-define-property: 1.0.1 + side-channel: 1.1.1 + + range-parser@1.3.0: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.3 + unpipe: 1.0.0 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@4.1.2: {} + + reflect-metadata@0.2.2: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + router@2.2.0(supports-color@8.1.1): + dependencies: + debug: 4.4.3(supports-color@8.1.1) + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + + rxjs@7.8.1: + dependencies: + tslib: 2.8.1 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + schema-utils@3.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.15.0 + ajv-keywords: 3.5.2(ajv@6.15.0) + + schema-utils@4.5.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + ajv-keywords: 5.1.0(ajv@8.20.0) + + semver@7.8.5: {} + + send@1.2.1(supports-color@8.1.1): + dependencies: + debug: 4.4.3(supports-color@8.1.1) + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.3.0 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1(supports-color@8.1.1): + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + statuses@2.0.2: {} + + streamsearch@1.1.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-bom@3.0.0: {} + + strtok3@10.3.5: + dependencies: + '@tokenizer/token': 0.3.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + symbol-observable@4.0.0: {} + + tapable@2.3.3: {} + + terser-webpack-plugin@5.6.1(webpack@5.106.2): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.5.0 + terser: 5.51.2 + webpack: 5.106.2 + + terser@5.51.2: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.19.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + toidentifier@1.0.1: {} + + token-types@6.1.2: + dependencies: + '@borewit/text-codec': 0.2.2 + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths-webpack-plugin@4.2.0: + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.26.0 + tapable: 2.3.3 + tsconfig-paths: 4.2.0 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + type-is@2.1.0: + dependencies: + content-type: 2.1.0 + media-typer: 1.1.1 + mime-types: 3.0.2 + + typescript-eslint@8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.71.1(@typescript-eslint/parser@8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3))(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.71.1(supports-color@8.1.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.71.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) + eslint: 10.12.0(supports-color@8.1.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + uid@2.0.2: + dependencies: + '@lukeed/csprng': 1.1.0 + + uint8array-extras@1.6.0: {} + + undici-types@7.24.6: {} + + universalify@2.0.1: {} + + unpipe@1.0.0: {} + + update-browserslist-db@1.3.4(browserslist@4.29.3): + dependencies: + browserslist: 4.29.3 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + vary@1.1.2: {} + + watchpack@2.5.2: + dependencies: + graceful-fs: 4.2.11 + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + webpack-node-externals@3.0.0: {} + + webpack-sources@3.6.0: {} + + webpack@5.106.2: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.9 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.19.0 + acorn-import-phases: 1.0.4(acorn@8.19.0) + browserslist: 4.29.3 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.26.0 + es-module-lexer: 2.3.2 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + loader-runner: 4.3.2 + mime-db: 1.54.0 + neo-async: 2.6.2 + schema-utils: 4.5.0 + tapable: 2.3.3 + terser-webpack-plugin: 5.6.1(webpack@5.106.2) + watchpack: 2.5.2 + webpack-sources: 3.6.0 + transitivePeerDependencies: + - '@minify-html/node' + - '@swc/core' + - '@swc/css' + - '@swc/html' + - clean-css + - cssnano + - csso + - esbuild + - html-minifier-terser + - lightningcss + - postcss + - uglify-js + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + yargs-parser@21.1.1: {} + + yocto-queue@0.1.0: {} + + yoctocolors-cjs@2.1.3: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..4373ce6 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,10 @@ +packages: + - 'apps/*' + - 'packages/*' + +engineStrict: true +strictPeerDependencies: true + +# NestJS 的安装后提示脚本不是运行必需项,不执行依赖安装脚本。 +allowBuilds: + '@nestjs/core': false diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..2851843 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "ES2022", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true + } +}