feat: 完成邮件发送服务集成

- 实现完整的邮件发送服务类,支持重试机制和错误处理

- 集成5种邮件模板(激活、密码重置、欢迎、通知、密码修改)

- 创建Elysia邮件服务插件,支持便捷方法调用

- 添加完整的TypeScript类型定义和配置文件

- 编写全面的测试用例,覆盖所有功能

- 支持SMTP连接池、健康检查和状态监控

关联M2基础用户系统开发任务
This commit is contained in:
HeXiaoLong:Suanier 2025-07-04 18:17:31 +08:00
parent 200c3b0b10
commit 0a74b7fb35
13 changed files with 3349 additions and 9 deletions

View File

@ -1,15 +1,6 @@
--- ---
description:
globs:
alwaysApply: true alwaysApply: true
--- ---
---
**文档2任务清单管理 (Task List Management)**
```markdown
---
描述 (description): 描述 (description):
全局模式 (globs): 全局模式 (globs):
始终应用 (alwaysApply): false 始终应用 (alwaysApply): false

779
bun.lock Normal file
View File

@ -0,0 +1,779 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "cursor-init",
"dependencies": {
"@elysiajs/jwt": "^1.3.1",
"@elysiajs/swagger": "^1.3.0",
"@types/ua-parser-js": "^0.7.39",
"chalk": "^5.4.1",
"drizzle-orm": "^0.44.2",
"mysql2": "^3.14.1",
"nanoid": "^5.1.5",
"nodemailer": "^7.0.4",
"picocolors": "^1.1.1",
"redis": "^5.5.6",
"ua-parser-js": "^2.0.4",
"undici": "^7.11.0",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0",
},
"devDependencies": {
"@types/bun": "^1.0.25",
"@types/nodemailer": "^6.4.17",
"@types/redis": "^4.0.11",
"@types/winston": "^2.4.4",
"@typescript-eslint/eslint-plugin": "^8.35.0",
"@typescript-eslint/parser": "^8.35.0",
"drizzle-kit": "^0.31.4",
"eslint": "^9.29.0",
"eslint-config-prettier": "^10.1.5",
"prettier": "^3.6.2",
"typescript": "^5.8.3",
"vitest": "^3.2.4",
},
},
},
"packages": {
"@colors/colors": ["@colors/colors@1.6.0", "https://registry.npmmirror.com/@colors/colors/-/colors-1.6.0.tgz", {}, "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="],
"@dabh/diagnostics": ["@dabh/diagnostics@2.0.3", "https://registry.npmmirror.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", { "dependencies": { "colorspace": "1.1.x", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA=="],
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "https://registry.npmmirror.com/@drizzle-team/brocli/-/brocli-0.10.2.tgz", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
"@elysiajs/jwt": ["@elysiajs/jwt@1.3.1", "https://registry.npmmirror.com/@elysiajs/jwt/-/jwt-1.3.1.tgz", { "dependencies": { "jose": "^6.0.11" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-BVLAp0ER4839bR82ElgTsI7OoPxvFWP5u02KMgqpNoAM6xirJBYTKqANzY9ghuMfQoVEuD4B1/8lZwdPKAVg9Q=="],
"@elysiajs/swagger": ["@elysiajs/swagger@1.3.0", "https://registry.npmmirror.com/@elysiajs/swagger/-/swagger-1.3.0.tgz", { "dependencies": { "@scalar/themes": "^0.9.52", "@scalar/types": "^0.0.12", "openapi-types": "^12.1.3", "pathe": "^1.1.2" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-0fo3FWkDRPNYpowJvLz3jBHe9bFe6gruZUyf+feKvUEEMG9ZHptO1jolSoPE0ffFw1BgN1/wMsP19p4GRXKdfg=="],
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "https://registry.npmmirror.com/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
"@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "https://registry.npmmirror.com/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.5", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.5", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.5", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.5", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.5", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.5", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.5", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.5", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.5", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.5", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.5", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.5", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.5", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.5", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.5", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.5", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.5", "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", { "os": "none", "cpu": "arm64" }, "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.5", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.5", "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", { "os": "openbsd", "cpu": "arm64" }, "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.5", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.5", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.5", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.5", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.5", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="],
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
"@eslint/config-array": ["@eslint/config-array@0.20.1", "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.20.1.tgz", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw=="],
"@eslint/config-helpers": ["@eslint/config-helpers@0.2.3", "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.2.3.tgz", {}, "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg=="],
"@eslint/core": ["@eslint/core@0.14.0", "https://registry.npmmirror.com/@eslint/core/-/core-0.14.0.tgz", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg=="],
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
"@eslint/js": ["@eslint/js@9.29.0", "https://registry.npmmirror.com/@eslint/js/-/js-9.29.0.tgz", {}, "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.6", "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.6.tgz", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.3", "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", { "dependencies": { "@eslint/core": "^0.15.1", "levn": "^0.4.1" } }, "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
"@humanfs/node": ["@humanfs/node@0.16.6", "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.6.tgz", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"@redis/bloom": ["@redis/bloom@5.5.6", "https://registry.npmmirror.com/@redis/bloom/-/bloom-5.5.6.tgz", { "peerDependencies": { "@redis/client": "^5.5.6" } }, "sha512-bNR3mxkwtfuCxNOzfV8B3R5zA1LiN57EH6zK4jVBIgzMzliNuReZXBFGnXvsi80/SYohajn78YdpYI+XNpqL+A=="],
"@redis/client": ["@redis/client@5.5.6", "https://registry.npmmirror.com/@redis/client/-/client-5.5.6.tgz", { "dependencies": { "cluster-key-slot": "1.1.2" } }, "sha512-M3Svdwt6oSfyfQdqEr0L2HOJH2vK7GgCFx1NfAQvpWAT4+ljoT1L5S5cKT3dA9NJrxrOPDkdoTPWJnIrGCOcmw=="],
"@redis/json": ["@redis/json@5.5.6", "https://registry.npmmirror.com/@redis/json/-/json-5.5.6.tgz", { "peerDependencies": { "@redis/client": "^5.5.6" } }, "sha512-AIsoe3SsGQagqAmSQHaqxEinm5oCWr7zxPWL90kKaEdLJ+zw8KBznf2i9oK0WUFP5pFssSQUXqnscQKe2amfDQ=="],
"@redis/search": ["@redis/search@5.5.6", "https://registry.npmmirror.com/@redis/search/-/search-5.5.6.tgz", { "peerDependencies": { "@redis/client": "^5.5.6" } }, "sha512-JSqasYqO0mVcHL7oxvbySRBBZYRYhFl3W7f0Da7BW8M/r0Z9wCiVrdjnN4/mKBpWZkoJT/iuisLUdPGhpKxBew=="],
"@redis/time-series": ["@redis/time-series@5.5.6", "https://registry.npmmirror.com/@redis/time-series/-/time-series-5.5.6.tgz", { "peerDependencies": { "@redis/client": "^5.5.6" } }, "sha512-jkpcgq3NOI3TX7xEAJ3JgesJTxAx7k0m6lNxNsYdEM8KOl+xj7GaB/0CbLkoricZDmFSEAz7ClA1iK9XkGHf+Q=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz", { "os": "android", "cpu": "arm" }, "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g=="],
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew=="],
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw=="],
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.44.1", "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug=="],
"@scalar/openapi-types": ["@scalar/openapi-types@0.1.1", "https://registry.npmmirror.com/@scalar/openapi-types/-/openapi-types-0.1.1.tgz", {}, "sha512-NMy3QNk6ytcCoPUGJH0t4NNr36OWXgZhA3ormr3TvhX1NDgoF95wFyodGVH8xiHeUyn2/FxtETm8UBLbB5xEmg=="],
"@scalar/themes": ["@scalar/themes@0.9.86", "https://registry.npmmirror.com/@scalar/themes/-/themes-0.9.86.tgz", { "dependencies": { "@scalar/types": "0.1.7" } }, "sha512-QUHo9g5oSWi+0Lm1vJY9TaMZRau8LHg+vte7q5BVTBnu6NuQfigCaN+ouQ73FqIVd96TwMO6Db+dilK1B+9row=="],
"@scalar/types": ["@scalar/types@0.0.12", "https://registry.npmmirror.com/@scalar/types/-/types-0.0.12.tgz", { "dependencies": { "@scalar/openapi-types": "0.1.1", "@unhead/schema": "^1.9.5" } }, "sha512-XYZ36lSEx87i4gDqopQlGCOkdIITHHEvgkuJFrXFATQs9zHARop0PN0g4RZYWj+ZpCUclOcaOjbCt8JGe22mnQ=="],
"@sinclair/typebox": ["@sinclair/typebox@0.34.37", "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.34.37.tgz", {}, "sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw=="],
"@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "https://registry.npmmirror.com/@tokenizer/inflate/-/inflate-0.2.7.tgz", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="],
"@tokenizer/token": ["@tokenizer/token@0.3.0", "https://registry.npmmirror.com/@tokenizer/token/-/token-0.3.0.tgz", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
"@types/bun": ["@types/bun@1.2.17", "https://registry.npmmirror.com/@types/bun/-/bun-1.2.17.tgz", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="],
"@types/chai": ["@types/chai@5.2.2", "https://registry.npmmirror.com/@types/chai/-/chai-5.2.2.tgz", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="],
"@types/deep-eql": ["@types/deep-eql@4.0.2", "https://registry.npmmirror.com/@types/deep-eql/-/deep-eql-4.0.2.tgz", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
"@types/estree": ["@types/estree@1.0.8", "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/node": ["@types/node@24.0.4", "https://registry.npmmirror.com/@types/node/-/node-24.0.4.tgz", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA=="],
"@types/node-fetch": ["@types/node-fetch@2.6.12", "https://registry.npmmirror.com/@types/node-fetch/-/node-fetch-2.6.12.tgz", { "dependencies": { "@types/node": "*", "form-data": "^4.0.0" } }, "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA=="],
"@types/nodemailer": ["@types/nodemailer@6.4.17", "https://registry.npmmirror.com/@types/nodemailer/-/nodemailer-6.4.17.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww=="],
"@types/redis": ["@types/redis@4.0.11", "https://registry.npmmirror.com/@types/redis/-/redis-4.0.11.tgz", { "dependencies": { "redis": "*" } }, "sha512-bI+gth8La8Wg/QCR1+V1fhrL9+LZUSWfcqpOj2Kc80ZQ4ffbdL173vQd5wovmoV9i071FU9oP2g6etLuEwb6Rg=="],
"@types/triple-beam": ["@types/triple-beam@1.3.5", "https://registry.npmmirror.com/@types/triple-beam/-/triple-beam-1.3.5.tgz", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="],
"@types/ua-parser-js": ["@types/ua-parser-js@0.7.39", "https://registry.npmmirror.com/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", {}, "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg=="],
"@types/winston": ["@types/winston@2.4.4", "https://registry.npmmirror.com/@types/winston/-/winston-2.4.4.tgz", { "dependencies": { "winston": "*" } }, "sha512-BVGCztsypW8EYwJ+Hq+QNYiT/MUyCif0ouBH+flrY66O5W+KIXAMML6E/0fJpm7VjIzgangahl5S03bJJQGrZw=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.35.0", "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.0.tgz", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.35.0", "@typescript-eslint/type-utils": "8.35.0", "@typescript-eslint/utils": "8.35.0", "@typescript-eslint/visitor-keys": "8.35.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.35.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.35.0", "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.35.0.tgz", { "dependencies": { "@typescript-eslint/scope-manager": "8.35.0", "@typescript-eslint/types": "8.35.0", "@typescript-eslint/typescript-estree": "8.35.0", "@typescript-eslint/visitor-keys": "8.35.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.35.0", "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.35.0.tgz", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.35.0", "@typescript-eslint/types": "^8.35.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.35.0", "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.35.0.tgz", { "dependencies": { "@typescript-eslint/types": "8.35.0", "@typescript-eslint/visitor-keys": "8.35.0" } }, "sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.35.0", "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.0.tgz", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.35.0", "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.35.0.tgz", { "dependencies": { "@typescript-eslint/typescript-estree": "8.35.0", "@typescript-eslint/utils": "8.35.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-ceNNttjfmSEoM9PW87bWLDEIaLAyR+E6BoYJQ5PfaDau37UGca9Nyq3lBk8Bw2ad0AKvYabz6wxc7DMTO2jnNA=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.35.0", "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.35.0.tgz", {}, "sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.35.0", "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.0.tgz", { "dependencies": { "@typescript-eslint/project-service": "8.35.0", "@typescript-eslint/tsconfig-utils": "8.35.0", "@typescript-eslint/types": "8.35.0", "@typescript-eslint/visitor-keys": "8.35.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.35.0", "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.35.0.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.35.0", "@typescript-eslint/types": "8.35.0", "@typescript-eslint/typescript-estree": "8.35.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.35.0", "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.0.tgz", { "dependencies": { "@typescript-eslint/types": "8.35.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g=="],
"@unhead/schema": ["@unhead/schema@1.11.20", "https://registry.npmmirror.com/@unhead/schema/-/schema-1.11.20.tgz", { "dependencies": { "hookable": "^5.5.3", "zhead": "^2.2.4" } }, "sha512-0zWykKAaJdm+/Y7yi/Yds20PrUK7XabLe9c3IRcjnwYmSWY6z0Cr19VIs3ozCj8P+GhR+/TI2mwtGlueCEYouA=="],
"@vitest/expect": ["@vitest/expect@3.2.4", "https://registry.npmmirror.com/@vitest/expect/-/expect-3.2.4.tgz", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
"@vitest/mocker": ["@vitest/mocker@3.2.4", "https://registry.npmmirror.com/@vitest/mocker/-/mocker-3.2.4.tgz", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="],
"@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
"@vitest/runner": ["@vitest/runner@3.2.4", "https://registry.npmmirror.com/@vitest/runner/-/runner-3.2.4.tgz", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="],
"@vitest/snapshot": ["@vitest/snapshot@3.2.4", "https://registry.npmmirror.com/@vitest/snapshot/-/snapshot-3.2.4.tgz", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="],
"@vitest/spy": ["@vitest/spy@3.2.4", "https://registry.npmmirror.com/@vitest/spy/-/spy-3.2.4.tgz", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="],
"@vitest/utils": ["@vitest/utils@3.2.4", "https://registry.npmmirror.com/@vitest/utils/-/utils-3.2.4.tgz", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
"acorn": ["acorn@8.15.0", "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
"ajv": ["ajv@6.12.6", "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
"ansi-styles": ["ansi-styles@4.3.0", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"argparse": ["argparse@2.0.1", "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"assertion-error": ["assertion-error@2.0.1", "https://registry.npmmirror.com/assertion-error/-/assertion-error-2.0.1.tgz", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
"async": ["async@3.2.6", "https://registry.npmmirror.com/async/-/async-3.2.6.tgz", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
"asynckit": ["asynckit@0.4.0", "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "https://registry.npmmirror.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
"balanced-match": ["balanced-match@1.0.2", "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"brace-expansion": ["brace-expansion@1.1.12", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"braces": ["braces@3.0.3", "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"buffer-from": ["buffer-from@1.1.2", "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"bun-types": ["bun-types@1.2.17", "https://registry.npmmirror.com/bun-types/-/bun-types-1.2.17.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="],
"cac": ["cac@6.7.14", "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"callsites": ["callsites@3.1.0", "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"chai": ["chai@5.2.0", "https://registry.npmmirror.com/chai/-/chai-5.2.0.tgz", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw=="],
"chalk": ["chalk@5.4.1", "https://registry.npmmirror.com/chalk/-/chalk-5.4.1.tgz", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
"check-error": ["check-error@2.1.1", "https://registry.npmmirror.com/check-error/-/check-error-2.1.1.tgz", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="],
"cluster-key-slot": ["cluster-key-slot@1.1.2", "https://registry.npmmirror.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="],
"color": ["color@3.2.1", "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", { "dependencies": { "color-convert": "^1.9.3", "color-string": "^1.6.0" } }, "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA=="],
"color-convert": ["color-convert@2.0.1", "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"color-string": ["color-string@1.9.1", "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
"colorspace": ["colorspace@1.1.4", "https://registry.npmmirror.com/colorspace/-/colorspace-1.1.4.tgz", { "dependencies": { "color": "^3.1.3", "text-hex": "1.0.x" } }, "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w=="],
"combined-stream": ["combined-stream@1.0.8", "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"concat-map": ["concat-map@0.0.1", "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"cookie": ["cookie@1.0.2", "https://registry.npmmirror.com/cookie/-/cookie-1.0.2.tgz", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
"cross-spawn": ["cross-spawn@7.0.6", "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"debug": ["debug@4.4.1", "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
"deep-eql": ["deep-eql@5.0.2", "https://registry.npmmirror.com/deep-eql/-/deep-eql-5.0.2.tgz", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
"deep-is": ["deep-is@0.1.4", "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
"delayed-stream": ["delayed-stream@1.0.0", "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"denque": ["denque@2.1.0", "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
"detect-europe-js": ["detect-europe-js@0.1.2", "https://registry.npmmirror.com/detect-europe-js/-/detect-europe-js-0.1.2.tgz", {}, "sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow=="],
"drizzle-kit": ["drizzle-kit@0.31.4", "https://registry.npmmirror.com/drizzle-kit/-/drizzle-kit-0.31.4.tgz", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA=="],
"drizzle-orm": ["drizzle-orm@0.44.2", "https://registry.npmmirror.com/drizzle-orm/-/drizzle-orm-0.44.2.tgz", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ=="],
"dunder-proto": ["dunder-proto@1.0.1", "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"elysia": ["elysia@1.3.5", "https://registry.npmmirror.com/elysia/-/elysia-1.3.5.tgz", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.1.2", "fast-decode-uri-component": "^1.0.1" }, "optionalDependencies": { "@sinclair/typebox": "^0.34.33", "openapi-types": "^12.1.3" }, "peerDependencies": { "file-type": ">= 20.0.0", "typescript": ">= 5.0.0" } }, "sha512-XVIKXlKFwUT7Sta8GY+wO5reD9I0rqAEtaz1Z71UgJb61csYt8Q3W9al8rtL5RgumuRR8e3DNdzlUN9GkC4KDw=="],
"enabled": ["enabled@2.0.0", "https://registry.npmmirror.com/enabled/-/enabled-2.0.0.tgz", {}, "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="],
"es-define-property": ["es-define-property@1.0.1", "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-module-lexer": ["es-module-lexer@1.7.0", "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
"esbuild": ["esbuild@0.25.5", "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="],
"esbuild-register": ["esbuild-register@3.6.0", "https://registry.npmmirror.com/esbuild-register/-/esbuild-register-3.6.0.tgz", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.29.0", "https://registry.npmmirror.com/eslint/-/eslint-9.29.0.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.1", "@eslint/config-helpers": "^0.2.1", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.29.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ=="],
"eslint-config-prettier": ["eslint-config-prettier@10.1.5", "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw=="],
"eslint-scope": ["eslint-scope@8.4.0", "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.4.0.tgz", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
"espree": ["espree@10.4.0", "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
"esquery": ["esquery@1.6.0", "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
"esrecurse": ["esrecurse@4.3.0", "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
"estraverse": ["estraverse@5.3.0", "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"estree-walker": ["estree-walker@3.0.3", "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"esutils": ["esutils@2.0.3", "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
"exact-mirror": ["exact-mirror@0.1.2", "https://registry.npmmirror.com/exact-mirror/-/exact-mirror-0.1.2.tgz", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-wFCPCDLmHbKGUb8TOi/IS7jLsgR8WVDGtDK3CzcB4Guf/weq7G+I+DkXiRSZfbemBFOxOINKpraM6ml78vo8Zw=="],
"expect-type": ["expect-type@1.2.1", "https://registry.npmmirror.com/expect-type/-/expect-type-1.2.1.tgz", {}, "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw=="],
"fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "https://registry.npmmirror.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-glob": ["fast-glob@3.3.3", "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
"fast-levenshtein": ["fast-levenshtein@2.0.6", "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
"fastq": ["fastq@1.19.1", "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
"fdir": ["fdir@6.4.6", "https://registry.npmmirror.com/fdir/-/fdir-6.4.6.tgz", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="],
"fecha": ["fecha@4.2.3", "https://registry.npmmirror.com/fecha/-/fecha-4.2.3.tgz", {}, "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="],
"fflate": ["fflate@0.8.2", "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
"file-stream-rotator": ["file-stream-rotator@0.6.1", "https://registry.npmmirror.com/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", { "dependencies": { "moment": "^2.29.1" } }, "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ=="],
"file-type": ["file-type@21.0.0", "https://registry.npmmirror.com/file-type/-/file-type-21.0.0.tgz", { "dependencies": { "@tokenizer/inflate": "^0.2.7", "strtok3": "^10.2.2", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg=="],
"fill-range": ["fill-range@7.1.1", "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
"find-up": ["find-up@5.0.0", "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
"flat-cache": ["flat-cache@4.0.1", "https://registry.npmmirror.com/flat-cache/-/flat-cache-4.0.1.tgz", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
"flatted": ["flatted@3.3.3", "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
"fn.name": ["fn.name@1.1.0", "https://registry.npmmirror.com/fn.name/-/fn.name-1.1.0.tgz", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="],
"form-data": ["form-data@4.0.3", "https://registry.npmmirror.com/form-data/-/form-data-4.0.3.tgz", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA=="],
"fsevents": ["fsevents@2.3.3", "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"function-bind": ["function-bind@1.1.2", "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"generate-function": ["generate-function@2.3.1", "https://registry.npmmirror.com/generate-function/-/generate-function-2.3.1.tgz", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-proto": ["get-proto@1.0.1", "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"get-tsconfig": ["get-tsconfig@4.10.1", "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.10.1.tgz", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
"glob-parent": ["glob-parent@6.0.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
"globals": ["globals@14.0.0", "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
"gopd": ["gopd@1.2.0", "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"graphemer": ["graphemer@1.4.0", "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
"has-flag": ["has-flag@4.0.0", "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"has-symbols": ["has-symbols@1.1.0", "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"has-tostringtag": ["has-tostringtag@1.0.2", "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
"hasown": ["hasown@2.0.2", "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"hookable": ["hookable@5.5.3", "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
"iconv-lite": ["iconv-lite@0.6.3", "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"ieee754": ["ieee754@1.2.1", "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"ignore": ["ignore@7.0.5", "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"import-fresh": ["import-fresh@3.3.1", "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"imurmurhash": ["imurmurhash@0.1.4", "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"inherits": ["inherits@2.0.4", "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"is-arrayish": ["is-arrayish@0.3.2", "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
"is-extglob": ["is-extglob@2.1.1", "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-glob": ["is-glob@4.0.3", "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-number": ["is-number@7.0.0", "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-property": ["is-property@1.0.2", "https://registry.npmmirror.com/is-property/-/is-property-1.0.2.tgz", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="],
"is-standalone-pwa": ["is-standalone-pwa@0.1.1", "https://registry.npmmirror.com/is-standalone-pwa/-/is-standalone-pwa-0.1.1.tgz", {}, "sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g=="],
"is-stream": ["is-stream@2.0.1", "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
"isexe": ["isexe@2.0.0", "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jose": ["jose@6.0.11", "https://registry.npmmirror.com/jose/-/jose-6.0.11.tgz", {}, "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg=="],
"js-tokens": ["js-tokens@9.0.1", "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
"js-yaml": ["js-yaml@4.1.0", "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
"json-buffer": ["json-buffer@3.0.1", "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
"json-schema-traverse": ["json-schema-traverse@0.4.1", "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
"keyv": ["keyv@4.5.4", "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"kuler": ["kuler@2.0.0", "https://registry.npmmirror.com/kuler/-/kuler-2.0.0.tgz", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="],
"levn": ["levn@0.4.1", "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
"locate-path": ["locate-path@6.0.0", "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lodash.merge": ["lodash.merge@4.6.2", "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
"logform": ["logform@2.7.0", "https://registry.npmmirror.com/logform/-/logform-2.7.0.tgz", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ=="],
"long": ["long@5.3.2", "https://registry.npmmirror.com/long/-/long-5.3.2.tgz", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
"loupe": ["loupe@3.1.4", "https://registry.npmmirror.com/loupe/-/loupe-3.1.4.tgz", {}, "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg=="],
"lru-cache": ["lru-cache@7.18.3", "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
"lru.min": ["lru.min@1.1.2", "https://registry.npmmirror.com/lru.min/-/lru.min-1.1.2.tgz", {}, "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg=="],
"magic-string": ["magic-string@0.30.17", "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"merge2": ["merge2@1.4.1", "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
"micromatch": ["micromatch@4.0.8", "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"mime-db": ["mime-db@1.52.0", "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"minimatch": ["minimatch@3.1.2", "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"moment": ["moment@2.30.1", "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="],
"ms": ["ms@2.1.3", "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"mysql2": ["mysql2@3.14.1", "https://registry.npmmirror.com/mysql2/-/mysql2-3.14.1.tgz", { "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.6.3", "long": "^5.2.1", "lru.min": "^1.0.0", "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" } }, "sha512-7ytuPQJjQB8TNAYX/H2yhL+iQOnIBjAMam361R7UAL0lOVXWjtdrmoL9HYKqKoLp/8UUTRcvo1QPvK9KL7wA8w=="],
"named-placeholders": ["named-placeholders@1.1.3", "https://registry.npmmirror.com/named-placeholders/-/named-placeholders-1.1.3.tgz", { "dependencies": { "lru-cache": "^7.14.1" } }, "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w=="],
"nanoid": ["nanoid@5.1.5", "https://registry.npmmirror.com/nanoid/-/nanoid-5.1.5.tgz", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw=="],
"natural-compare": ["natural-compare@1.4.0", "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"node-fetch": ["node-fetch@2.7.0", "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"nodemailer": ["nodemailer@7.0.4", "https://registry.npmmirror.com/nodemailer/-/nodemailer-7.0.4.tgz", {}, "sha512-9O00Vh89/Ld2EcVCqJ/etd7u20UhME0f/NToPfArwPEe1Don1zy4mAIz6ariRr7mJ2RDxtaDzN0WJVdVXPtZaw=="],
"object-hash": ["object-hash@3.0.0", "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
"one-time": ["one-time@1.0.0", "https://registry.npmmirror.com/one-time/-/one-time-1.0.0.tgz", { "dependencies": { "fn.name": "1.x.x" } }, "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g=="],
"openapi-types": ["openapi-types@12.1.3", "https://registry.npmmirror.com/openapi-types/-/openapi-types-12.1.3.tgz", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
"optionator": ["optionator@0.9.4", "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
"p-limit": ["p-limit@3.1.0", "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
"p-locate": ["p-locate@5.0.0", "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
"parent-module": ["parent-module@1.0.1", "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"path-exists": ["path-exists@4.0.0", "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-key": ["path-key@3.1.1", "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"pathe": ["pathe@2.0.3", "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"pathval": ["pathval@2.0.1", "https://registry.npmmirror.com/pathval/-/pathval-2.0.1.tgz", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="],
"picocolors": ["picocolors@1.1.1", "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.2", "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"postcss": ["postcss@8.5.6", "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
"prelude-ls": ["prelude-ls@1.2.1", "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"prettier": ["prettier@3.6.2", "https://registry.npmmirror.com/prettier/-/prettier-3.6.2.tgz", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
"punycode": ["punycode@2.3.1", "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"queue-microtask": ["queue-microtask@1.2.3", "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"readable-stream": ["readable-stream@3.6.2", "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"redis": ["redis@5.5.6", "https://registry.npmmirror.com/redis/-/redis-5.5.6.tgz", { "dependencies": { "@redis/bloom": "5.5.6", "@redis/client": "5.5.6", "@redis/json": "5.5.6", "@redis/search": "5.5.6", "@redis/time-series": "5.5.6" } }, "sha512-hbpqBfcuhWHOS9YLNcXcJ4akNr7HFX61Dq3JuFZ9S7uU7C7kvnzuH2PDIXOP62A3eevvACoG8UacuXP3N07xdg=="],
"resolve-from": ["resolve-from@4.0.0", "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
"reusify": ["reusify@1.1.0", "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
"rollup": ["rollup@4.44.1", "https://registry.npmmirror.com/rollup/-/rollup-4.44.1.tgz", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.1", "@rollup/rollup-android-arm64": "4.44.1", "@rollup/rollup-darwin-arm64": "4.44.1", "@rollup/rollup-darwin-x64": "4.44.1", "@rollup/rollup-freebsd-arm64": "4.44.1", "@rollup/rollup-freebsd-x64": "4.44.1", "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", "@rollup/rollup-linux-arm-musleabihf": "4.44.1", "@rollup/rollup-linux-arm64-gnu": "4.44.1", "@rollup/rollup-linux-arm64-musl": "4.44.1", "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", "@rollup/rollup-linux-riscv64-gnu": "4.44.1", "@rollup/rollup-linux-riscv64-musl": "4.44.1", "@rollup/rollup-linux-s390x-gnu": "4.44.1", "@rollup/rollup-linux-x64-gnu": "4.44.1", "@rollup/rollup-linux-x64-musl": "4.44.1", "@rollup/rollup-win32-arm64-msvc": "4.44.1", "@rollup/rollup-win32-ia32-msvc": "4.44.1", "@rollup/rollup-win32-x64-msvc": "4.44.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg=="],
"run-parallel": ["run-parallel@1.2.0", "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
"safe-buffer": ["safe-buffer@5.2.1", "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"safe-stable-stringify": ["safe-stable-stringify@2.5.0", "https://registry.npmmirror.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
"safer-buffer": ["safer-buffer@2.1.2", "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"semver": ["semver@7.7.2", "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
"seq-queue": ["seq-queue@0.0.5", "https://registry.npmmirror.com/seq-queue/-/seq-queue-0.0.5.tgz", {}, "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="],
"shebang-command": ["shebang-command@2.0.0", "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"siginfo": ["siginfo@2.0.0", "https://registry.npmmirror.com/siginfo/-/siginfo-2.0.0.tgz", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
"simple-swizzle": ["simple-swizzle@0.2.2", "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
"source-map": ["source-map@0.6.1", "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"source-map-js": ["source-map-js@1.2.1", "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"source-map-support": ["source-map-support@0.5.21", "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
"sqlstring": ["sqlstring@2.3.3", "https://registry.npmmirror.com/sqlstring/-/sqlstring-2.3.3.tgz", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="],
"stack-trace": ["stack-trace@0.0.10", "https://registry.npmmirror.com/stack-trace/-/stack-trace-0.0.10.tgz", {}, "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="],
"stackback": ["stackback@0.0.2", "https://registry.npmmirror.com/stackback/-/stackback-0.0.2.tgz", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
"std-env": ["std-env@3.9.0", "https://registry.npmmirror.com/std-env/-/std-env-3.9.0.tgz", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
"string_decoder": ["string_decoder@1.3.0", "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
"strip-json-comments": ["strip-json-comments@3.1.1", "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"strip-literal": ["strip-literal@3.0.0", "https://registry.npmmirror.com/strip-literal/-/strip-literal-3.0.0.tgz", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="],
"strtok3": ["strtok3@10.3.1", "https://registry.npmmirror.com/strtok3/-/strtok3-10.3.1.tgz", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw=="],
"supports-color": ["supports-color@7.2.0", "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"text-hex": ["text-hex@1.0.0", "https://registry.npmmirror.com/text-hex/-/text-hex-1.0.0.tgz", {}, "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="],
"tinybench": ["tinybench@2.9.0", "https://registry.npmmirror.com/tinybench/-/tinybench-2.9.0.tgz", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
"tinyexec": ["tinyexec@0.3.2", "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
"tinyglobby": ["tinyglobby@0.2.14", "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"tinypool": ["tinypool@1.1.1", "https://registry.npmmirror.com/tinypool/-/tinypool-1.1.1.tgz", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="],
"tinyrainbow": ["tinyrainbow@2.0.0", "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="],
"tinyspy": ["tinyspy@4.0.3", "https://registry.npmmirror.com/tinyspy/-/tinyspy-4.0.3.tgz", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="],
"to-regex-range": ["to-regex-range@5.0.1", "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"token-types": ["token-types@6.0.0", "https://registry.npmmirror.com/token-types/-/token-types-6.0.0.tgz", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA=="],
"tr46": ["tr46@0.0.3", "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
"triple-beam": ["triple-beam@1.4.1", "https://registry.npmmirror.com/triple-beam/-/triple-beam-1.4.1.tgz", {}, "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg=="],
"ts-api-utils": ["ts-api-utils@2.1.0", "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
"type-check": ["type-check@0.4.0", "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
"type-fest": ["type-fest@4.41.0", "https://registry.npmmirror.com/type-fest/-/type-fest-4.41.0.tgz", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
"typescript": ["typescript@5.8.3", "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
"ua-is-frozen": ["ua-is-frozen@0.1.2", "https://registry.npmmirror.com/ua-is-frozen/-/ua-is-frozen-0.1.2.tgz", {}, "sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw=="],
"ua-parser-js": ["ua-parser-js@2.0.4", "https://registry.npmmirror.com/ua-parser-js/-/ua-parser-js-2.0.4.tgz", { "dependencies": { "@types/node-fetch": "^2.6.12", "detect-europe-js": "^0.1.2", "is-standalone-pwa": "^0.1.1", "node-fetch": "^2.7.0", "ua-is-frozen": "^0.1.2" }, "bin": { "ua-parser-js": "script/cli.js" } }, "sha512-XiBOnM/UpUq21ZZ91q2AVDOnGROE6UQd37WrO9WBgw4u2eGvUCNOheMmZ3EfEUj7DLHr8tre+Um/436Of/Vwzg=="],
"uint8array-extras": ["uint8array-extras@1.4.0", "https://registry.npmmirror.com/uint8array-extras/-/uint8array-extras-1.4.0.tgz", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="],
"undici": ["undici@7.11.0", "https://registry.npmmirror.com/undici/-/undici-7.11.0.tgz", {}, "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg=="],
"undici-types": ["undici-types@7.8.0", "https://registry.npmmirror.com/undici-types/-/undici-types-7.8.0.tgz", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
"uri-js": ["uri-js@4.4.1", "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"util-deprecate": ["util-deprecate@1.0.2", "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"vite": ["vite@7.0.0", "https://registry.npmmirror.com/vite/-/vite-7.0.0.tgz", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.2", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g=="],
"vite-node": ["vite-node@3.2.4", "https://registry.npmmirror.com/vite-node/-/vite-node-3.2.4.tgz", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
"vitest": ["vitest@3.2.4", "https://registry.npmmirror.com/vitest/-/vitest-3.2.4.tgz", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
"whatwg-url": ["whatwg-url@5.0.0", "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
"which": ["which@2.0.2", "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"why-is-node-running": ["why-is-node-running@2.3.0", "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
"winston": ["winston@3.17.0", "https://registry.npmmirror.com/winston/-/winston-3.17.0.tgz", { "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.9.0" } }, "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw=="],
"winston-daily-rotate-file": ["winston-daily-rotate-file@5.0.0", "https://registry.npmmirror.com/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz", { "dependencies": { "file-stream-rotator": "^0.6.1", "object-hash": "^3.0.0", "triple-beam": "^1.4.1", "winston-transport": "^4.7.0" }, "peerDependencies": { "winston": "^3" } }, "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw=="],
"winston-transport": ["winston-transport@4.9.0", "https://registry.npmmirror.com/winston-transport/-/winston-transport-4.9.0.tgz", { "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" } }, "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A=="],
"word-wrap": ["word-wrap@1.2.5", "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
"yocto-queue": ["yocto-queue@0.1.0", "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"zhead": ["zhead@2.2.4", "https://registry.npmmirror.com/zhead/-/zhead-2.2.4.tgz", {}, "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag=="],
"zod": ["zod@3.25.67", "https://registry.npmmirror.com/zod/-/zod-3.25.67.tgz", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="],
"@elysiajs/swagger/pathe": ["pathe@1.1.2", "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "https://registry.npmmirror.com/esbuild/-/esbuild-0.18.20.tgz", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@eslint/eslintrc/ignore": ["ignore@5.3.2", "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.15.1", "https://registry.npmmirror.com/@eslint/core/-/core-0.15.1.tgz", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA=="],
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.3.1.tgz", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
"@scalar/themes/@scalar/types": ["@scalar/types@0.1.7", "https://registry.npmmirror.com/@scalar/types/-/types-0.1.7.tgz", { "dependencies": { "@scalar/openapi-types": "0.2.0", "@unhead/schema": "^1.11.11", "nanoid": "^5.1.5", "type-fest": "^4.20.0", "zod": "^3.23.8" } }, "sha512-irIDYzTQG2KLvFbuTI8k2Pz/R4JR+zUUSykVTbEMatkzMmVFnn1VzNSMlODbadycwZunbnL2tA27AXed9URVjw=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"color/color-convert": ["color-convert@1.9.3", "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"eslint/chalk": ["chalk@4.1.2", "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"eslint/ignore": ["ignore@5.3.2", "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"micromatch/picomatch": ["picomatch@2.3.1", "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"postcss/nanoid": ["nanoid@3.3.11", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="],
"@scalar/themes/@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "https://registry.npmmirror.com/@scalar/openapi-types/-/openapi-types-0.2.0.tgz", { "dependencies": { "zod": "^3.23.8" } }, "sha512-waiKk12cRCqyUCWTOX0K1WEVX46+hVUK+zRPzAahDJ7G0TApvbNkuy5wx7aoUyEk++HHde0XuQnshXnt8jsddA=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"color/color-convert/color-name": ["color-name@1.1.3", "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
}
}

View File

@ -15,6 +15,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "^1.0.25", "@types/bun": "^1.0.25",
"@types/nodemailer": "^6.4.17",
"@types/redis": "^4.0.11", "@types/redis": "^4.0.11",
"@types/winston": "^2.4.4", "@types/winston": "^2.4.4",
"@typescript-eslint/eslint-plugin": "^8.35.0", "@typescript-eslint/eslint-plugin": "^8.35.0",
@ -34,6 +35,7 @@
"drizzle-orm": "^0.44.2", "drizzle-orm": "^0.44.2",
"mysql2": "^3.14.1", "mysql2": "^3.14.1",
"nanoid": "^5.1.5", "nanoid": "^5.1.5",
"nodemailer": "^7.0.4",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"redis": "^5.5.6", "redis": "^5.5.6",
"ua-parser-js": "^2.0.4", "ua-parser-js": "^2.0.4",

128
src/config/email.config.ts Normal file
View File

@ -0,0 +1,128 @@
/**
* @file
* @author hotok
* @date 2025-06-29
* @lastEditor hotok
* @lastEditTime 2025-06-29
* @description
*/
/**
* SMTP服务器配置
* @property {string} host - SMTP服务器主机地址
* @property {number} port - SMTP服务器端口号
* @property {boolean} secure - 使SSL/TLS
* @property {object} auth -
*/
export const smtpConfig = {
/** SMTP服务器主机地址 */
host: process.env.SMTP_HOST || 'smtp.qq.com',
/** SMTP服务器端口号 */
port: Number(process.env.SMTP_PORT) || 587,
/** 是否使用SSL/TLS */
secure: process.env.SMTP_SECURE === 'true' || false,
/** 认证信息 */
auth: {
user: process.env.SMTP_USER || '',
pass: process.env.SMTP_PASS || '',
},
/** 连接超时时间(毫秒) */
connectionTimeout: Number(process.env.SMTP_TIMEOUT) || 60000,
/** 问候超时时间(毫秒) */
greetingTimeout: Number(process.env.SMTP_GREETING_TIMEOUT) || 30000,
/** 套接字超时时间(毫秒) */
socketTimeout: Number(process.env.SMTP_SOCKET_TIMEOUT) || 60000,
};
/**
*
* @property {string} from -
* @property {string} replyTo -
* @property {string} charset -
*/
export const emailConfig = {
/** 发件人信息 */
from: process.env.EMAIL_FROM || `"星撰系统" <${smtpConfig.auth.user}>`,
/** 回复邮箱 */
replyTo: process.env.EMAIL_REPLY_TO || smtpConfig.auth.user,
/** 字符编码 */
charset: 'utf-8',
/** 邮件优先级 */
priority: 'normal' as const,
};
/**
*
* @property {object} activation -
* @property {object} passwordReset -
* @property {object} welcome -
* @property {object} notification -
*/
export const emailTemplates = {
/** 账号激活邮件模板 */
activation: {
subject: '请激活您的账户 - 星撰系统',
template: 'activation',
expireTime: 24 * 60 * 60 * 1000, // 24小时
},
/** 密码重置邮件模板 */
passwordReset: {
subject: '重置您的密码 - 星撰系统',
template: 'password-reset',
expireTime: 30 * 60 * 1000, // 30分钟
},
/** 欢迎邮件模板 */
welcome: {
subject: '欢迎加入星撰系统',
template: 'welcome',
},
/** 通知邮件模板 */
notification: {
subject: '系统通知 - 星撰系统',
template: 'notification',
},
/** 密码修改通知模板 */
passwordChanged: {
subject: '密码已修改 - 星撰系统',
template: 'password-changed',
},
};
/**
*
* @property {number} retryAttempts -
* @property {number} retryDelay -
* @property {boolean} enableQueue -
*/
export const emailOptions = {
/** 重试次数 */
retryAttempts: Number(process.env.EMAIL_RETRY_ATTEMPTS) || 3,
/** 重试延迟时间(毫秒) */
retryDelay: Number(process.env.EMAIL_RETRY_DELAY) || 5000,
/** 是否启用队列发送 */
enableQueue: process.env.EMAIL_ENABLE_QUEUE === 'true' || false,
/** 单次发送最大收件人数量 */
maxRecipients: Number(process.env.EMAIL_MAX_RECIPIENTS) || 50,
/** 发送频率限制(邮件/分钟) */
rateLimit: Number(process.env.EMAIL_RATE_LIMIT) || 100,
};
/**
*
* @returns {boolean}
*/
export const validateEmailConfig = (): boolean => {
const { host, auth } = smtpConfig;
return !!(host && auth.user && auth.pass);
};
/**
*
* @returns
*/
export const getEmailConfig = () => ({
smtp: smtpConfig,
email: emailConfig,
templates: emailTemplates,
options: emailOptions,
});

View File

@ -1,4 +1,5 @@
export * from './db.config'; export * from './db.config';
export * from './email.config';
export * from './jwt.config'; export * from './jwt.config';
export * from './logger.config'; export * from './logger.config';
export * from './redis.config'; export * from './redis.config';

217
src/plugins/email/README.md Normal file
View File

@ -0,0 +1,217 @@
# 邮件服务插件
邮件服务插件为 Elysia 应用提供完整的邮件发送功能,包括模板邮件、重试机制、健康检查等。
## 特性
- 🚀 **易于使用**: 简单的 API 接口,支持链式调用
- 📧 **模板支持**: 内置 5 种邮件模板(激活、密码重置、欢迎、通知、密码修改)
- 🔄 **重试机制**: 自动重试失败的邮件发送
- 📊 **健康检查**: 提供 SMTP 连接状态监控
- 🎨 **美观模板**: 响应式 HTML 邮件模板
- 🔒 **类型安全**: 完整的 TypeScript 类型支持
- 📝 **详细日志**: 完整的邮件发送日志记录
## 安装和配置
### 1. 环境变量配置
`.env` 文件中配置邮件服务参数:
```env
# SMTP 服务器配置
SMTP_HOST=smtp.qq.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@qq.com
SMTP_PASS=your-password
# 邮件基础配置
EMAIL_FROM="星撰系统 <your-email@qq.com>"
EMAIL_REPLY_TO=your-email@qq.com
# 重试配置
EMAIL_RETRY_ATTEMPTS=3
EMAIL_RETRY_DELAY=5000
```
### 2. 插件注册
在 Elysia 应用中注册邮件插件:
```typescript
import { Elysia } from 'elysia';
import { emailPlugin } from '@/plugins/email/email.plugins';
const app = new Elysia()
.use(emailPlugin)
// ... 其他插件
```
## 使用方法
### 基础邮件发送
```typescript
app.post('/send-email', async ({ emailService }) => {
const result = await emailService.sendEmail({
to: 'user@example.com',
subject: '测试邮件',
html: '<h1>Hello World!</h1>',
text: 'Hello World!',
});
return { success: result.success, messageId: result.messageId };
});
```
### 模板邮件发送
#### 1. 用户激活邮件
```typescript
app.post('/send-activation', async ({ emailService }) => {
return await emailService.sendActivationEmail({
to: 'user@example.com',
username: 'john_doe',
nickname: 'John',
activationUrl: 'https://example.com/activate?token=xxx',
activationCode: '123456',
expireTime: '24小时',
});
});
```
#### 2. 密码重置邮件
```typescript
app.post('/send-reset', async ({ emailService }) => {
return await emailService.sendPasswordResetEmail({
to: 'user@example.com',
username: 'john_doe',
nickname: 'John',
resetUrl: 'https://example.com/reset?token=xxx',
resetCode: '123456',
expireTime: '30分钟',
});
});
```
#### 3. 欢迎邮件
```typescript
app.post('/send-welcome', async ({ emailService }) => {
return await emailService.sendWelcomeEmail({
to: 'user@example.com',
username: 'john_doe',
nickname: 'John',
});
});
```
#### 4. 密码修改通知
```typescript
app.post('/send-password-changed', async ({ emailService }) => {
return await emailService.sendPasswordChangedEmail({
to: 'user@example.com',
username: 'john_doe',
nickname: 'John',
});
});
```
#### 5. 系统通知邮件
```typescript
app.post('/send-notification', async ({ emailService }) => {
return await emailService.sendNotificationEmail({
to: ['user1@example.com', 'user2@example.com'],
username: 'john_doe',
message: '您有新的系统消息,请及时查看。',
subject: '重要通知',
});
});
```
### 服务状态检查
```typescript
app.get('/email/status', ({ emailService }) => {
return {
status: emailService.getStatus(),
initialized: emailService.isInitialized(),
};
});
app.get('/email/health', async ({ emailService }) => {
return await emailService.healthCheck();
});
```
## API 参考
### emailService.sendEmail(options)
发送自定义邮件。
**参数:**
- `options.to`: 收件人邮箱(字符串或数组)
- `options.subject`: 邮件主题
- `options.html`: HTML 内容
- `options.text`: 纯文本内容
- `options.cc`: 抄送邮箱(可选)
- `options.bcc`: 密送邮箱(可选)
- `options.attachments`: 附件列表(可选)
- `options.priority`: 邮件优先级(可选)
### emailService.sendTemplateEmail(options)
发送模板邮件。
**参数:**
- `options.to`: 收件人邮箱
- `options.template`: 模板类型
- `options.params`: 模板参数
- `options.subject`: 自定义主题(可选)
- `options.priority`: 邮件优先级(可选)
### 便捷方法
- `sendActivationEmail()`: 发送激活邮件
- `sendPasswordResetEmail()`: 发送密码重置邮件
- `sendWelcomeEmail()`: 发送欢迎邮件
- `sendPasswordChangedEmail()`: 发送密码修改通知
- `sendNotificationEmail()`: 发送系统通知
### 状态检查
- `getStatus()`: 获取服务状态
- `healthCheck()`: 执行健康检查
- `isInitialized()`: 检查是否已初始化
## 错误处理
插件内置了完善的错误处理机制:
- **配置验证**: 启动时验证 SMTP 配置
- **连接检查**: 健康检查验证 SMTP 连接
- **重试机制**: 自动重试失败的邮件发送
- **优雅降级**: 邮件服务故障不影响应用启动
## 日志记录
所有邮件操作都会记录详细日志:
- 邮件发送成功/失败
- 重试次数和原因
- 响应时间
- 错误详情
## 注意事项
1. **SMTP 认证**: 确保 SMTP 用户名和密码正确
2. **端口配置**: 根据邮件提供商配置正确的端口和 SSL 设置
3. **发送限制**: 注意邮件提供商的发送频率限制
4. **模板自定义**: 可以通过修改服务类来自定义邮件模板
5. **安全性**: 避免在邮件内容中包含敏感信息

View File

@ -0,0 +1,242 @@
/**
* @file
* @author hotok
* @date 2025-06-29
* @lastEditor hotok
* @lastEditTime 2025-06-29
* @description Elysia
*/
import { Elysia } from 'elysia';
import { emailService, initializeEmailService } from './email.service';
import { validateEmailConfig } from '@/config/email.config';
import { Logger } from '@/plugins/logger/logger.service';
import type {
EmailSendOptions,
EmailTemplateSendOptions,
EmailSendResult,
EmailServiceStatus,
EmailHealthCheckResult,
} from '@/type/email.type';
/**
*
*
*/
export const emailPlugin = new Elysia({ name: 'email' })
.onStart(async () => {
try {
// 验证邮件配置
if (!validateEmailConfig()) {
Logger.warn('邮件配置不完整,跳过邮件服务初始化');
return;
}
// 初始化邮件服务
await initializeEmailService();
Logger.info('邮件服务插件加载完成');
} catch (error) {
Logger.error(new Error(`邮件服务插件初始化失败: ${error}`));
// 不抛出错误,允许应用继续启动(邮件服务为非核心服务)
}
})
.decorate('emailService', {
/**
*
*/
async sendEmail(options: EmailSendOptions): Promise<EmailSendResult> {
return await emailService.sendEmail(options);
},
/**
*
*/
async sendTemplateEmail(options: EmailTemplateSendOptions): Promise<EmailSendResult> {
return await emailService.sendTemplateEmail(options);
},
/**
*
*/
getStatus(): EmailServiceStatus {
return emailService.status;
},
/**
*
*/
async healthCheck(): Promise<EmailHealthCheckResult> {
return await emailService.healthCheck();
},
/**
*
*/
isInitialized(): boolean {
return emailService.isInitialized;
},
/**
*
*/
async sendActivationEmail(options: {
to: string;
username: string;
nickname?: string;
activationUrl: string;
activationCode: string;
expireTime: string;
}): Promise<EmailSendResult> {
return await emailService.sendTemplateEmail({
to: options.to,
template: 'activation',
params: {
username: options.username,
nickname: options.nickname,
email: options.to,
activationUrl: options.activationUrl,
activationCode: options.activationCode,
expireTime: options.expireTime,
},
});
},
/**
*
*/
async sendPasswordResetEmail(options: {
to: string;
username: string;
nickname?: string;
resetUrl: string;
resetCode: string;
expireTime: string;
}): Promise<EmailSendResult> {
return await emailService.sendTemplateEmail({
to: options.to,
template: 'passwordReset',
params: {
username: options.username,
nickname: options.nickname,
email: options.to,
resetUrl: options.resetUrl,
resetCode: options.resetCode,
expireTime: options.expireTime,
},
});
},
/**
*
*/
async sendWelcomeEmail(options: {
to: string;
username: string;
nickname?: string;
}): Promise<EmailSendResult> {
return await emailService.sendTemplateEmail({
to: options.to,
template: 'welcome',
params: {
username: options.username,
nickname: options.nickname,
email: options.to,
},
});
},
/**
*
*/
async sendPasswordChangedEmail(options: {
to: string;
username: string;
nickname?: string;
}): Promise<EmailSendResult> {
return await emailService.sendTemplateEmail({
to: options.to,
template: 'passwordChanged',
params: {
username: options.username,
nickname: options.nickname,
email: options.to,
},
});
},
/**
*
*/
async sendNotificationEmail(options: {
to: string | string[];
username?: string;
nickname?: string;
message: string;
subject?: string;
}): Promise<EmailSendResult> {
return await emailService.sendTemplateEmail({
to: options.to,
template: 'notification',
params: {
username: options.username,
nickname: options.nickname,
message: options.message,
},
subject: options.subject,
});
},
})
.onStop(async () => {
try {
await emailService.close();
Logger.info('邮件服务已关闭');
} catch (error) {
Logger.error(new Error(`关闭邮件服务时出错: ${error}`));
}
});
/**
* TypeScript类型推断
*/
export type EmailServiceDecorator = {
emailService: {
sendEmail(options: EmailSendOptions): Promise<EmailSendResult>;
sendTemplateEmail(options: EmailTemplateSendOptions): Promise<EmailSendResult>;
getStatus(): EmailServiceStatus;
healthCheck(): Promise<EmailHealthCheckResult>;
isInitialized(): boolean;
sendActivationEmail(options: {
to: string;
username: string;
nickname?: string;
activationUrl: string;
activationCode: string;
expireTime: string;
}): Promise<EmailSendResult>;
sendPasswordResetEmail(options: {
to: string;
username: string;
nickname?: string;
resetUrl: string;
resetCode: string;
expireTime: string;
}): Promise<EmailSendResult>;
sendWelcomeEmail(options: {
to: string;
username: string;
nickname?: string;
}): Promise<EmailSendResult>;
sendPasswordChangedEmail(options: {
to: string;
username: string;
nickname?: string;
}): Promise<EmailSendResult>;
sendNotificationEmail(options: {
to: string | string[];
username?: string;
nickname?: string;
message: string;
subject?: string;
}): Promise<EmailSendResult>;
};
};

View File

@ -0,0 +1,610 @@
/**
* @file
* @author hotok
* @date 2025-06-29
* @lastEditor hotok
* @lastEditTime 2025-06-29
* @description
*/
import nodemailer from 'nodemailer';
import { smtpConfig, emailConfig, emailTemplates, emailOptions } from '@/config/email.config';
import { Logger } from '@/plugins/logger/logger.service';
import type {
EmailTransporter,
EmailSendOptions,
EmailTemplateSendOptions,
EmailSendResult,
EmailServiceStatus,
EmailHealthCheckResult,
EmailTemplateType,
EmailTemplateParams,
} from '@/type/email.type';
/**
*
* 使
*/
export class EmailService {
/** 单例实例 */
private static instance: EmailService | null = null;
/** 邮件传输器实例 */
private _transporter: EmailTransporter | null = null;
/** 服务状态信息 */
private _status: EmailServiceStatus;
/** 初始化标志 */
private _isInitialized = false;
/**
*
*/
private constructor() {
this._status = {
status: 'unhealthy',
transporterStatus: 'disconnected',
error: undefined,
lastCheckAt: new Date(),
};
}
/**
*
*/
public static getInstance(): EmailService {
if (!EmailService.instance) {
EmailService.instance = new EmailService();
}
return EmailService.instance;
}
/**
*
*/
public get transporter(): EmailTransporter {
if (!this._transporter) {
throw new Error('邮件服务未初始化,请先调用 initialize() 方法');
}
return this._transporter;
}
/**
*
*/
public get status(): EmailServiceStatus {
return { ...this._status };
}
/**
*
*/
public get isInitialized(): boolean {
return this._isInitialized;
}
/**
*
*/
private validateConfig(): void {
if (!smtpConfig.host || !smtpConfig.port) {
throw new Error('SMTP配置无效缺少host或port');
}
if (!smtpConfig.auth.user || !smtpConfig.auth.pass) {
throw new Error('SMTP配置无效缺少用户名或密码');
}
if (smtpConfig.port < 1 || smtpConfig.port > 65535) {
throw new Error(`SMTP端口号无效: ${smtpConfig.port}`);
}
}
/**
*
*/
private updateStatus(
status: EmailServiceStatus['status'],
transporterStatus: EmailServiceStatus['transporterStatus'],
error?: string
): void {
this._status = {
status,
transporterStatus,
error,
lastCheckAt: new Date(),
};
}
/**
*
*/
public async initialize(): Promise<EmailTransporter> {
// 防止重复初始化
if (this._isInitialized && this._transporter) {
Logger.info('邮件服务已初始化,返回现有实例');
return this._transporter;
}
try {
this.validateConfig();
this.updateStatus('unhealthy', 'disconnected');
// 创建邮件传输器
this._transporter = nodemailer.createTransport({
host: smtpConfig.host,
port: smtpConfig.port,
secure: smtpConfig.secure,
auth: {
user: smtpConfig.auth.user,
pass: smtpConfig.auth.pass,
},
connectionTimeout: smtpConfig.connectionTimeout,
greetingTimeout: smtpConfig.greetingTimeout,
socketTimeout: smtpConfig.socketTimeout,
pool: true, // 使用连接池
maxConnections: 5, // 最大连接数
maxMessages: 100, // 每个连接最大消息数
});
// 验证SMTP连接
await this._transporter.verify();
this._isInitialized = true;
this.updateStatus('healthy', 'connected');
Logger.info({
message: '邮件服务初始化成功',
host: smtpConfig.host,
port: smtpConfig.port,
secure: smtpConfig.secure,
user: smtpConfig.auth.user,
});
return this._transporter;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.updateStatus('unhealthy', 'error', errorMessage);
Logger.error(new Error(`邮件服务初始化失败: ${errorMessage}`));
throw new Error(`邮件服务初始化失败: ${errorMessage}`);
}
}
/**
*
*/
public async sendEmail(options: EmailSendOptions): Promise<EmailSendResult> {
const startTime = Date.now();
let retryCount = 0;
while (retryCount <= emailOptions.retryAttempts) {
try {
if (!this._transporter) {
throw new Error('邮件传输器未初始化');
}
// 准备邮件选项
const mailOptions = {
from: options.from || emailConfig.from,
to: Array.isArray(options.to) ? options.to.join(', ') : options.to,
cc: options.cc ? (Array.isArray(options.cc) ? options.cc.join(', ') : options.cc) : undefined,
bcc: options.bcc ? (Array.isArray(options.bcc) ? options.bcc.join(', ') : options.bcc) : undefined,
subject: options.subject,
text: options.text,
html: options.html,
replyTo: options.replyTo || emailConfig.replyTo,
priority: options.priority || emailConfig.priority,
attachments: options.attachments,
headers: options.headers,
messageId: options.messageId,
references: options.references,
inReplyTo: options.inReplyTo,
};
// 发送邮件
const result = await this._transporter.sendMail(mailOptions);
const sendResult: EmailSendResult = {
success: true,
messageId: result.messageId,
accepted: result.accepted || [],
rejected: result.rejected || [],
pending: result.pending || [],
response: result.response,
sentAt: new Date(),
retryCount,
};
Logger.info({
message: '邮件发送成功',
messageId: result.messageId,
to: options.to,
subject: options.subject,
responseTime: Date.now() - startTime,
retryCount,
});
return sendResult;
} catch (error) {
retryCount++;
const errorMessage = error instanceof Error ? error.message : String(error);
Logger.warn({
message: '邮件发送失败',
error: errorMessage,
to: options.to,
subject: options.subject,
retryCount,
maxRetries: emailOptions.retryAttempts,
});
// 如果已达到最大重试次数,返回失败结果
if (retryCount > emailOptions.retryAttempts) {
return {
success: false,
accepted: [],
rejected: Array.isArray(options.to) ? options.to : [options.to],
pending: [],
error: errorMessage,
sentAt: new Date(),
retryCount: retryCount - 1,
};
}
// 等待重试延迟
await new Promise(resolve => setTimeout(resolve, emailOptions.retryDelay));
}
}
// 这个分支理论上不会执行,但为了类型安全
return {
success: false,
accepted: [],
rejected: Array.isArray(options.to) ? options.to : [options.to],
pending: [],
error: '未知错误',
sentAt: new Date(),
retryCount: emailOptions.retryAttempts,
};
}
/**
*
*/
public async sendTemplateEmail(options: EmailTemplateSendOptions): Promise<EmailSendResult> {
try {
const template = emailTemplates[options.template];
if (!template) {
throw new Error(`未找到邮件模板: ${options.template}`);
}
// 渲染邮件内容
const { subject, html, text } = this.renderTemplate(options.template, options.params);
// 构建发送选项
const sendOptions: EmailSendOptions = {
to: options.to,
cc: options.cc,
bcc: options.bcc,
subject: options.subject || subject,
html,
text,
priority: options.priority,
attachments: options.attachments,
};
return await this.sendEmail(sendOptions);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
Logger.error(new Error(`模板邮件发送失败: ${errorMessage}`));
return {
success: false,
accepted: [],
rejected: Array.isArray(options.to) ? options.to : [options.to],
pending: [],
error: errorMessage,
sentAt: new Date(),
retryCount: 0,
};
}
}
/**
*
*/
private renderTemplate(
templateType: EmailTemplateType,
params: EmailTemplateParams
): { subject: string; html: string; text: string } {
const template = emailTemplates[templateType];
const defaultParams = {
systemName: '星撰系统',
companyName: '星撰科技',
supportEmail: smtpConfig.auth.user,
...params,
};
let subject = template.subject;
let html = '';
let text = '';
switch (templateType) {
case 'activation':
subject = template.subject;
html = this.getActivationTemplate(defaultParams);
text = `您好 ${defaultParams.nickname || defaultParams.username}\n\n请点击以下链接激活您的账户\n${defaultParams.activationUrl}\n\n或使用激活码${defaultParams.activationCode}\n\n链接将在 ${defaultParams.expireTime} 后过期。\n\n如果您没有注册账户请忽略此邮件。\n\n${defaultParams.systemName}`;
break;
case 'passwordReset':
subject = emailTemplates.passwordReset.subject;
html = this.getPasswordResetTemplate(defaultParams);
text = `您好 ${defaultParams.nickname || defaultParams.username}\n\n我们收到了重置您账户密码的请求。请点击以下链接重置密码\n${defaultParams.resetUrl}\n\n或使用重置码${defaultParams.resetCode}\n\n链接将在 ${defaultParams.expireTime} 后过期。\n\n如果您没有请求重置密码请忽略此邮件。\n\n${defaultParams.systemName}`;
break;
case 'welcome':
subject = template.subject;
html = this.getWelcomeTemplate(defaultParams);
text = `欢迎 ${defaultParams.nickname || defaultParams.username}\n\n感谢您注册 ${defaultParams.systemName}。您的账户已成功激活。\n\n如果您有任何问题请联系我们${defaultParams.supportEmail}\n\n${defaultParams.systemName}`;
break;
case 'passwordChanged':
subject = emailTemplates.passwordChanged.subject;
html = this.getPasswordChangedTemplate(defaultParams);
text = `您好 ${defaultParams.nickname || defaultParams.username}\n\n您的账户密码已成功修改。如果这不是您本人的操作请立即联系我们。\n\n联系邮箱${defaultParams.supportEmail}\n\n${defaultParams.systemName}`;
break;
case 'notification':
subject = template.subject;
html = this.getNotificationTemplate(defaultParams);
text = (defaultParams as any).message || '您有新的系统通知';
break;
default:
throw new Error(`不支持的邮件模板类型: ${templateType}`);
}
return { subject, html, text };
}
/**
*
*/
private getActivationTemplate(params: EmailTemplateParams): string {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
<h2 style="color: #2563eb;"></h2>
<p> ${params.nickname || params.username}</p>
<p> ${params.systemName}</p>
<div style="text-align: center; margin: 30px 0;">
<a href="${params.activationUrl}" style="background-color: #2563eb; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; display: inline-block;"></a>
</div>
<p></p>
<p style="word-break: break-all; background-color: #f3f4f6; padding: 10px; border-radius: 5px;">${params.activationUrl}</p>
<p><strong>${params.activationCode}</strong></p>
<p><strong></strong> ${params.expireTime} </p>
<p></p>
<hr style="margin: 30px 0; border: none; border-top: 1px solid #e5e7eb;">
<p style="font-size: 14px; color: #6b7280;">
${params.systemName} <br>
${params.supportEmail}
</p>
</div>
</body>
</html>`;
}
/**
*
*/
private getPasswordResetTemplate(params: EmailTemplateParams): string {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
<h2 style="color: #dc2626;"></h2>
<p> ${params.nickname || params.username}</p>
<p></p>
<div style="text-align: center; margin: 30px 0;">
<a href="${params.resetUrl}" style="background-color: #dc2626; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; display: inline-block;"></a>
</div>
<p></p>
<p style="word-break: break-all; background-color: #f3f4f6; padding: 10px; border-radius: 5px;">${params.resetUrl}</p>
<p><strong>${params.resetCode}</strong></p>
<p><strong></strong> ${params.expireTime} </p>
<p></p>
<hr style="margin: 30px 0; border: none; border-top: 1px solid #e5e7eb;">
<p style="font-size: 14px; color: #6b7280;">
${params.systemName} <br>
${params.supportEmail}
</p>
</div>
</body>
</html>`;
}
/**
*
*/
private getWelcomeTemplate(params: EmailTemplateParams): string {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
<h2 style="color: #059669;"> ${params.systemName}</h2>
<p> ${params.nickname || params.username}</p>
<p> ${params.systemName}使</p>
<div style="background-color: #f0f9ff; padding: 20px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #0369a1; margin-top: 0;"></h3>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<p>使</p>
<hr style="margin: 30px 0; border: none; border-top: 1px solid #e5e7eb;">
<p style="font-size: 14px; color: #6b7280;">
${params.systemName} <br>
${params.supportEmail}
</p>
</div>
</body>
</html>`;
}
/**
*
*/
private getPasswordChangedTemplate(params: EmailTemplateParams): string {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
<h2 style="color: #f59e0b;"></h2>
<p> ${params.nickname || params.username}</p>
<p></p>
<div style="background-color: #fef3c7; padding: 15px; border-radius: 5px; margin: 20px 0; border-left: 4px solid #f59e0b;">
<p style="margin: 0;"><strong></strong> ${new Date().toLocaleString('zh-CN')}</p>
</div>
<p></p>
<ul>
<li>${params.supportEmail}</li>
<li></li>
<li></li>
</ul>
<hr style="margin: 30px 0; border: none; border-top: 1px solid #e5e7eb;">
<p style="font-size: 14px; color: #6b7280;">
${params.systemName} <br>
${params.supportEmail}
</p>
</div>
</body>
</html>`;
}
/**
*
*/
private getNotificationTemplate(params: EmailTemplateParams): string {
const message = (params as any).message || '您有新的系统通知,请登录查看详情。';
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
<h2 style="color: #6366f1;"></h2>
<p> ${params.nickname || params.username}</p>
<div style="background-color: #f8fafc; padding: 20px; border-radius: 5px; margin: 20px 0;">
${message}
</div>
<hr style="margin: 30px 0; border: none; border-top: 1px solid #e5e7eb;">
<p style="font-size: 14px; color: #6b7280;">
${params.systemName} <br>
${params.supportEmail}
</p>
</div>
</body>
</html>`;
}
/**
*
*/
public async healthCheck(): Promise<EmailHealthCheckResult> {
const startTime = Date.now();
try {
if (!this._transporter) {
return {
status: 'unhealthy',
responseTime: 0,
serviceStatus: this.status,
error: '邮件传输器未初始化',
};
}
// 验证SMTP连接
await this._transporter.verify();
const responseTime = Date.now() - startTime;
this.updateStatus('healthy', 'connected');
return {
status: 'healthy',
responseTime,
serviceStatus: this.status,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.updateStatus('unhealthy', 'error', errorMessage);
return {
status: 'unhealthy',
responseTime: Date.now() - startTime,
serviceStatus: this.status,
error: errorMessage,
};
}
}
/**
*
*/
public async close(): Promise<void> {
try {
if (this._transporter) {
this._transporter.close();
this._transporter = null;
}
this._isInitialized = false;
this.updateStatus('unhealthy', 'disconnected');
Logger.info('邮件服务已关闭');
} catch (error) {
Logger.error(error instanceof Error ? error : new Error('关闭邮件服务时出错'));
}
}
}
// 创建单例实例
const emailService = EmailService.getInstance();
// 导出便捷方法
export const initializeEmailService = () => emailService.initialize();
export const sendEmail = (options: EmailSendOptions) => emailService.sendEmail(options);
export const sendTemplateEmail = (options: EmailTemplateSendOptions) => emailService.sendTemplateEmail(options);
export const getEmailServiceStatus = () => emailService.status;
export const checkEmailServiceHealth = () => emailService.healthCheck();
export const closeEmailService = () => emailService.close();
// 导出服务实例
export { emailService };

View File

@ -13,6 +13,7 @@ import { errorHandlerPlugin } from '@/plugins/errorHandle/errorHandler.plugins';
import { swaggerPlugin } from '@/plugins/swagger/swagger.plugins'; import { swaggerPlugin } from '@/plugins/swagger/swagger.plugins';
import { drizzlePlugin } from '@/plugins/drizzle/drizzle.plugins'; import { drizzlePlugin } from '@/plugins/drizzle/drizzle.plugins';
import { redisPlugin } from '@/plugins/redis/redis.plugins'; import { redisPlugin } from '@/plugins/redis/redis.plugins';
import { emailPlugin } from '@/plugins/email/email.plugins';
export const plugins = (app: Elysia) => export const plugins = (app: Elysia) =>
app app
@ -24,5 +25,7 @@ export const plugins = (app: Elysia) =>
.use(drizzlePlugin) .use(drizzlePlugin)
// Redis插件 // Redis插件
.use(redisPlugin) .use(redisPlugin)
// 邮件服务插件
.use(emailPlugin)
// API 文档插件 // API 文档插件
.use(swaggerPlugin); .use(swaggerPlugin);

361
src/tests/email.test.ts Normal file
View File

@ -0,0 +1,361 @@
/**
* @file
* @author hotok
* @date 2025-06-29
* @lastEditor hotok
* @lastEditTime 2025-06-29
* @description
*/
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import { setTimeout } from 'node:timers';
import {
emailService,
initializeEmailService,
sendEmail,
sendTemplateEmail,
getEmailServiceStatus,
checkEmailServiceHealth,
closeEmailService
} from '@/plugins/email/email.service';
import { validateEmailConfig } from '@/config/email.config';
import type { EmailSendOptions, EmailTemplateSendOptions } from '@/type/email.type';
// 简单的延时函数
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
// 测试用的邮箱地址(请根据实际情况修改)
const TEST_EMAIL = 'test@example.com';
const TEST_USERNAME = 'test_user';
const TEST_NICKNAME = '测试用户';
describe('邮件服务测试', () => {
let isEmailConfigValid = false;
beforeAll(async () => {
// 检查邮件配置是否有效
isEmailConfigValid = validateEmailConfig();
if (isEmailConfigValid) {
try {
// 初始化邮件服务
await initializeEmailService();
console.log('邮件服务初始化成功');
} catch (error) {
console.warn('邮件服务初始化失败,某些测试将被跳过:', error);
isEmailConfigValid = false;
}
} else {
console.warn('邮件配置无效,邮件发送测试将被跳过');
}
});
afterAll(async () => {
if (isEmailConfigValid) {
await closeEmailService();
}
});
describe('邮件服务配置和初始化', () => {
it('应该验证邮件配置有效性', () => {
const isValid = validateEmailConfig();
// 不强制要求配置有效因为测试环境可能没有配置SMTP
expect(typeof isValid).toBe('boolean');
});
it('应该能够获取邮件服务状态', () => {
const status = getEmailServiceStatus();
expect(status).toHaveProperty('status');
expect(status).toHaveProperty('transporterStatus');
expect(status).toHaveProperty('lastCheckAt');
expect(['healthy', 'unhealthy', 'degraded']).toContain(status.status);
expect(['connected', 'disconnected', 'error']).toContain(status.transporterStatus);
});
it('应该能够检查服务初始化状态', () => {
const isInitialized = emailService.isInitialized;
expect(typeof isInitialized).toBe('boolean');
});
});
describe('邮件服务健康检查', () => {
it('应该执行健康检查并返回结果', async () => {
const healthResult = await checkEmailServiceHealth();
expect(healthResult).toHaveProperty('status');
expect(healthResult).toHaveProperty('responseTime');
expect(healthResult).toHaveProperty('serviceStatus');
expect(['healthy', 'unhealthy']).toContain(healthResult.status);
expect(typeof healthResult.responseTime).toBe('number');
expect(healthResult.responseTime).toBeGreaterThanOrEqual(0);
});
});
describe('邮件发送功能', () => {
// 只在邮件配置有效时运行实际发送测试
const runSendTest = isEmailConfigValid ? it : it.skip;
runSendTest('应该能够发送简单文本邮件', async () => {
const emailOptions: EmailSendOptions = {
to: TEST_EMAIL,
subject: '测试邮件 - 简单文本',
text: '这是一封测试邮件的纯文本内容。',
html: '<p>这是一封测试邮件的<strong>HTML内容</strong>。</p>',
};
const result = await sendEmail(emailOptions);
expect(result).toHaveProperty('success');
expect(result).toHaveProperty('sentAt');
expect(result.sentAt).toBeInstanceOf(Date);
if (result.success) {
expect(result).toHaveProperty('messageId');
expect(result.accepted).toContain(TEST_EMAIL);
console.log('邮件发送成功:', result.messageId);
} else {
console.warn('邮件发送失败:', result.error);
}
}, 10000); // 设置较长的超时时间
runSendTest('应该能够发送带抄送的邮件', async () => {
const emailOptions: EmailSendOptions = {
to: TEST_EMAIL,
cc: TEST_EMAIL, // 测试环境下抄送给同一个邮箱
subject: '测试邮件 - 带抄送',
html: '<h2>测试抄送功能</h2><p>这封邮件测试抄送功能。</p>',
};
const result = await sendEmail(emailOptions);
expect(result).toHaveProperty('success');
expect(result).toHaveProperty('sentAt');
}, 10000);
it('应该处理邮件发送错误', async () => {
const invalidEmailOptions: EmailSendOptions = {
to: 'invalid-email', // 无效邮箱地址
subject: '测试邮件 - 无效地址',
text: '这应该失败',
};
const result = await sendEmail(invalidEmailOptions);
// 无论SMTP是否配置无效邮箱都应该返回失败
expect(result).toHaveProperty('success');
expect(result).toHaveProperty('sentAt');
if (!result.success) {
expect(result).toHaveProperty('error');
expect(result.rejected).toContain('invalid-email');
}
});
});
describe('模板邮件功能', () => {
const runTemplateTest = isEmailConfigValid ? it : it.skip;
runTemplateTest('应该能够发送用户激活邮件', async () => {
const templateOptions: EmailTemplateSendOptions = {
to: TEST_EMAIL,
template: 'activation',
params: {
username: TEST_USERNAME,
nickname: TEST_NICKNAME,
email: TEST_EMAIL,
activationUrl: 'https://example.com/activate?token=test123',
activationCode: '123456',
expireTime: '24小时',
},
};
const result = await sendTemplateEmail(templateOptions);
expect(result).toHaveProperty('success');
expect(result).toHaveProperty('sentAt');
if (result.success) {
expect(result).toHaveProperty('messageId');
console.log('激活邮件发送成功:', result.messageId);
}
}, 10000);
runTemplateTest('应该能够发送密码重置邮件', async () => {
const templateOptions: EmailTemplateSendOptions = {
to: TEST_EMAIL,
template: 'passwordReset',
params: {
username: TEST_USERNAME,
nickname: TEST_NICKNAME,
email: TEST_EMAIL,
resetUrl: 'https://example.com/reset?token=test123',
resetCode: '654321',
expireTime: '30分钟',
},
};
const result = await sendTemplateEmail(templateOptions);
expect(result).toHaveProperty('success');
expect(result).toHaveProperty('sentAt');
if (result.success) {
console.log('密码重置邮件发送成功:', result.messageId);
}
}, 10000);
runTemplateTest('应该能够发送欢迎邮件', async () => {
const templateOptions: EmailTemplateSendOptions = {
to: TEST_EMAIL,
template: 'welcome',
params: {
username: TEST_USERNAME,
nickname: TEST_NICKNAME,
email: TEST_EMAIL,
},
};
const result = await sendTemplateEmail(templateOptions);
expect(result).toHaveProperty('success');
expect(result).toHaveProperty('sentAt');
if (result.success) {
console.log('欢迎邮件发送成功:', result.messageId);
}
}, 10000);
runTemplateTest('应该能够发送密码修改通知邮件', async () => {
const templateOptions: EmailTemplateSendOptions = {
to: TEST_EMAIL,
template: 'passwordChanged',
params: {
username: TEST_USERNAME,
nickname: TEST_NICKNAME,
email: TEST_EMAIL,
},
};
const result = await sendTemplateEmail(templateOptions);
expect(result).toHaveProperty('success');
expect(result).toHaveProperty('sentAt');
if (result.success) {
console.log('密码修改通知邮件发送成功:', result.messageId);
}
}, 10000);
runTemplateTest('应该能够发送系统通知邮件', async () => {
const templateOptions: EmailTemplateSendOptions = {
to: TEST_EMAIL,
template: 'notification',
params: {
username: TEST_USERNAME,
nickname: TEST_NICKNAME,
message: '这是一条重要的系统通知,请及时查看您的账户状态。',
},
subject: '重要系统通知',
};
const result = await sendTemplateEmail(templateOptions);
expect(result).toHaveProperty('success');
expect(result).toHaveProperty('sentAt');
if (result.success) {
console.log('系统通知邮件发送成功:', result.messageId);
}
}, 10000);
it('应该处理无效模板类型', async () => {
const invalidTemplateOptions = {
to: TEST_EMAIL,
template: 'invalid-template' as any,
params: {
username: TEST_USERNAME,
},
};
const result = await sendTemplateEmail(invalidTemplateOptions);
expect(result.success).toBe(false);
expect(result).toHaveProperty('error');
expect(result.error).toContain('未找到邮件模板');
});
});
describe('邮件服务错误处理', () => {
it('应该能够处理SMTP连接错误', async () => {
// 模拟SMTP连接失败的情况
const emailOptions: EmailSendOptions = {
to: TEST_EMAIL,
subject: '测试连接错误',
text: '这个测试用于验证错误处理',
};
const result = await sendEmail(emailOptions);
// 如果SMTP未配置或连接失败应该有适当的错误处理
expect(result).toHaveProperty('success');
expect(result).toHaveProperty('sentAt');
if (!result.success) {
expect(result).toHaveProperty('error');
expect(typeof result.error).toBe('string');
}
});
});
describe('邮件服务性能测试', () => {
const runPerformanceTest = isEmailConfigValid ? it : it.skip;
runPerformanceTest('应该能够并发发送多封邮件', async () => {
const emailPromises = [];
const emailCount = 3; // 限制并发数量以避免SMTP限制
for (let i = 0; i < emailCount; i++) {
const emailOptions: EmailSendOptions = {
to: TEST_EMAIL,
subject: `并发测试邮件 ${i + 1}`,
text: `这是第 ${i + 1} 封并发测试邮件`,
};
emailPromises.push(sendEmail(emailOptions));
}
const results = await Promise.all(emailPromises);
expect(results).toHaveLength(emailCount);
results.forEach((result, index) => {
expect(result).toHaveProperty('success');
expect(result).toHaveProperty('sentAt');
console.log(`邮件 ${index + 1} 发送结果:`, result.success ? '成功' : '失败');
});
}, 30000); // 更长的超时时间用于并发测试
});
describe('邮件模板渲染测试', () => {
it('应该正确渲染激活邮件模板内容', async () => {
const templateOptions: EmailTemplateSendOptions = {
to: TEST_EMAIL,
template: 'activation',
params: {
username: 'test_user',
nickname: '测试用户',
activationUrl: 'https://example.com/activate?token=abc123',
activationCode: '123456',
expireTime: '24小时',
},
};
// 我们可以通过发送结果来验证模板是否正确处理
const result = await sendTemplateEmail(templateOptions);
expect(result).toHaveProperty('success');
// 模板渲染错误会导致发送失败
if (!result.success && result.error) {
expect(result.error).not.toContain('模板渲染失败');
}
});
});
});

255
src/type/email.type.ts Normal file
View File

@ -0,0 +1,255 @@
/**
* @file
* @author hotok
* @date 2025-06-29
* @lastEditor hotok
* @lastEditTime 2025-06-29
* @description
*/
import type { Transporter } from 'nodemailer';
/**
*
*/
export type EmailTransporter = Transporter;
/**
*
*/
export type EmailPriority = 'high' | 'normal' | 'low';
/**
*
*/
export type EmailTemplateType = 'activation' | 'passwordReset' | 'welcome' | 'notification' | 'passwordChanged';
/**
*
*/
export interface EmailAttachment {
/** 文件名 */
filename: string;
/** 文件内容 */
content: string | Buffer;
/** 内容类型 */
contentType?: string;
/** 内容编码 */
encoding?: string;
/** 内容ID */
cid?: string;
/** 内容配置 */
contentDisposition?: 'attachment' | 'inline';
}
/**
*
*/
export interface EmailSendOptions {
/** 收件人邮箱数组 */
to: string | string[];
/** 抄送邮箱数组 */
cc?: string | string[];
/** 密送邮箱数组 */
bcc?: string | string[];
/** 邮件主题 */
subject: string;
/** 纯文本内容 */
text?: string;
/** HTML内容 */
html?: string;
/** 发件人 */
from?: string;
/** 回复邮箱 */
replyTo?: string;
/** 邮件优先级 */
priority?: EmailPriority;
/** 附件列表 */
attachments?: EmailAttachment[];
/** 邮件头信息 */
headers?: Record<string, string>;
/** 消息ID */
messageId?: string;
/** 引用消息ID */
references?: string;
/** 回复消息ID */
inReplyTo?: string;
}
/**
*
*/
export interface EmailTemplateParams {
/** 用户名 */
username?: string;
/** 用户昵称 */
nickname?: string;
/** 用户邮箱 */
email?: string;
/** 激活链接 */
activationUrl?: string;
/** 激活码 */
activationCode?: string;
/** 重置链接 */
resetUrl?: string;
/** 重置码 */
resetCode?: string;
/** 过期时间 */
expireTime?: string;
/** 系统名称 */
systemName?: string;
/** 公司名称 */
companyName?: string;
/** 支持邮箱 */
supportEmail?: string;
/** 其他自定义参数 */
[key: string]: any;
}
/**
*
*/
export interface EmailTemplateSendOptions {
/** 收件人邮箱数组 */
to: string | string[];
/** 模板类型 */
template: EmailTemplateType;
/** 模板参数 */
params: EmailTemplateParams;
/** 抄送邮箱数组 */
cc?: string | string[];
/** 密送邮箱数组 */
bcc?: string | string[];
/** 自定义主题(覆盖模板默认主题) */
subject?: string;
/** 邮件优先级 */
priority?: EmailPriority;
/** 附件列表 */
attachments?: EmailAttachment[];
/** 语言标识 */
locale?: string;
}
/**
*
*/
export interface EmailSendResult {
/** 是否发送成功 */
success: boolean;
/** 消息ID */
messageId?: string;
/** 发送的邮箱数量 */
accepted: string[];
/** 被拒绝的邮箱 */
rejected: string[];
/** 待处理的邮箱 */
pending: string[];
/** 响应信息 */
response?: string;
/** 错误信息 */
error?: string;
/** 发送时间 */
sentAt: Date;
/** 重试次数 */
retryCount?: number;
}
/**
*
*/
export interface EmailQueueTask {
/** 任务ID */
id: string;
/** 邮件选项 */
options: EmailSendOptions | EmailTemplateSendOptions;
/** 创建时间 */
createdAt: Date;
/** 计划发送时间 */
scheduledAt?: Date;
/** 重试次数 */
retryCount: number;
/** 最大重试次数 */
maxRetries: number;
/** 优先级 */
priority: number;
/** 任务状态 */
status: 'pending' | 'processing' | 'completed' | 'failed';
/** 错误信息 */
error?: string;
}
/**
*
*/
export interface EmailServiceConfig {
/** SMTP配置 */
smtp: {
host: string;
port: number;
secure: boolean;
auth: {
user: string;
pass: string;
};
connectionTimeout: number;
greetingTimeout: number;
socketTimeout: number;
};
/** 邮件基础配置 */
email: {
from: string;
replyTo: string;
charset: string;
priority: EmailPriority;
};
/** 发送选项 */
options: {
retryAttempts: number;
retryDelay: number;
enableQueue: boolean;
maxRecipients: number;
rateLimit: number;
};
}
/**
*
*/
export interface EmailServiceStatus {
/** 服务状态 */
status: 'healthy' | 'unhealthy' | 'degraded';
/** 传输器状态 */
transporterStatus: 'connected' | 'disconnected' | 'error';
/** 队列状态 */
queueStatus?: {
pending: number;
processing: number;
failed: number;
};
/** 错误信息 */
error?: string;
/** 最后检查时间 */
lastCheckAt: Date;
}
/**
* Elysia Context
*/
export interface EmailContext {
/** 邮件服务实例 */
emailService: any; // 这里会在service创建后更新为具体类型
}
/**
*
*/
export interface EmailHealthCheckResult {
/** 状态 */
status: 'healthy' | 'unhealthy';
/** 响应时间 */
responseTime: number;
/** 服务状态 */
serviceStatus: EmailServiceStatus;
/** 错误信息 */
error?: string;
}

View File

@ -0,0 +1,234 @@
# M2 - 基础用户系统 - 开发PRD
## 1. 项目概述
### 1.1 项目背景
基于星撰个人综合平台的M2阶段需求开发完整的基础用户系统。该系统将为后续的博客系统、个人空间、阅读系统等功能模块提供统一的用户管理和权限控制基础。
### 1.2 项目目标
- 构建安全可靠的用户认证和授权体系
- 实现灵活的角色和权限管理
- 提供完整的用户生命周期管理
- 建立可扩展的组织架构管理
- 为后续功能模块提供统一的用户服务
### 1.3 项目范围
本次开发包含以下核心模块:
- **认证模块**:注册、登录、激活、密码管理
- **用户管理模块**用户CRUD、信息管理、状态管理
- **角色权限模块**:角色管理、权限分配、权限验证
- **组织架构模块**:组织管理、用户组织关系
- **系统基础模块**:字典管理、标签管理、日志审计
## 2. 功能规划
### 2.1 核心功能清单
#### 2.1.1 认证模块 (Auth)
| 功能 | 优先级 | 状态 | 接口 |
|------|--------|------|------|
| 用户注册 | P0 | 待开发 | POST /auth/register |
| 邮箱激活 | P0 | 待开发 | POST /auth/activate |
| 用户登录 | P0 | 待开发 | POST /auth/login |
| Token刷新 | P0 | 待开发 | POST /auth/refresh |
| 退出登录 | P0 | 待开发 | POST /auth/logout |
| 找回密码 | P1 | 待开发 | POST /auth/password/reset-request |
| 重置密码 | P1 | 待开发 | POST /auth/password/reset-confirm |
| 图形验证码 | P1 | 待开发 | GET /auth/captcha |
#### 2.1.2 用户管理模块 (User)
| 功能 | 优先级 | 状态 | 接口 |
|------|--------|------|------|
| 获取当前用户信息 | P0 | 待开发 | GET /users/me |
| 用户列表查询 | P0 | 待开发 | GET /users |
| 创建用户 | P0 | 待开发 | POST /users |
| 更新用户信息 | P0 | 待开发 | PUT /users/{id} |
| 删除用户 | P1 | 待开发 | DELETE /users/{id} |
| 修改密码 | P0 | 待开发 | PUT /users/me/password |
| 用户详情 | P1 | 待开发 | GET /users/{id} |
| 批量操作 | P2 | 待开发 | POST /users/batch |
#### 2.1.3 角色权限模块 (Role & Permission)
| 功能 | 优先级 | 状态 | 接口 |
|------|--------|------|------|
| 角色列表 | P0 | 待开发 | GET /roles |
| 创建角色 | P0 | 待开发 | POST /roles |
| 更新角色 | P0 | 待开发 | PUT /roles/{id} |
| 删除角色 | P1 | 待开发 | DELETE /roles/{id} |
| 权限列表 | P0 | 待开发 | GET /permissions |
| 权限分配 | P0 | 待开发 | POST /roles/{id}/permissions |
| 用户角色分配 | P0 | 待开发 | POST /users/{id}/roles |
#### 2.1.4 组织架构模块 (Organization)
| 功能 | 优先级 | 状态 | 接口 |
|------|--------|------|------|
| 组织列表 | P1 | 待开发 | GET /organizations |
| 创建组织 | P1 | 待开发 | POST /organizations |
| 更新组织 | P1 | 待开发 | PUT /organizations/{id} |
| 删除组织 | P2 | 待开发 | DELETE /organizations/{id} |
| 用户组织关系 | P1 | 待开发 | POST /users/{id}/organizations |
#### 2.1.5 系统基础模块 (System)
| 功能 | 优先级 | 状态 | 接口 |
|------|--------|------|------|
| 字典类型管理 | P1 | 待开发 | CRUD /dict-types |
| 字典项管理 | P1 | 待开发 | CRUD /dict-items |
| 标签管理 | P1 | 待开发 | CRUD /tags |
| 操作日志 | P2 | 待开发 | GET /logs/operations |
| 登录日志 | P2 | 待开发 | GET /logs/logins |
### 2.2 技术架构
#### 2.2.1 后端架构
- **框架**Elysia (Bun)
- **数据库**MySQL + Drizzle ORM
- **缓存**Redis
- **认证**JWT + RefreshToken
- **日志**Winston
- **测试**Vitest
- **API文档**Swagger
#### 2.2.2 数据存储策略
- **主数据**MySQL存储
- **缓存数据**Redis存储用户会话、验证码、权限缓存
- **文件存储**:本地存储 + 后续可扩展OSS
#### 2.2.3 安全策略
- **密码加密**bcrypt (cost=12)
- **JWT策略**AccessToken(2小时) + RefreshToken(7天)
- **权限控制**RBAC模型
- **防暴力破解**:登录失败锁定
- **数据验证**:输入参数严格校验
## 3. 开发计划
### 3.1 阶段划分
#### 第一阶段:基础认证系统 (1-2周)
**目标**:完成用户注册、登录、基础用户管理功能
**核心任务**
- 数据库表结构完善
- 用户注册/登录接口
- JWT认证中间件
- 基础用户管理接口
- 单元测试编写
**交付物**
- 可工作的用户注册登录系统
- 完整的API文档
- 基础测试用例
#### 第二阶段:角色权限系统 (1-2周)
**目标**完成RBAC权限控制体系
**核心任务**
- 角色管理功能
- 权限管理功能
- 权限验证中间件
- 用户角色分配
- 权限缓存机制
**交付物**
- 完整的RBAC系统
- 权限控制中间件
- 管理员操作界面接口
#### 第三阶段:系统完善 (1周)
**目标**:完善系统功能,优化性能
**核心任务**
- 组织架构管理
- 字典标签管理
- 操作日志记录
- 性能优化
- 安全加固
**交付物**
- 完整的用户系统
- 性能测试报告
- 部署文档
### 3.2 里程碑计划
| 里程碑 | 时间节点 | 主要交付物 |
|--------|----------|------------|
| M2.1 | 第1周末 | 基础认证功能完成 |
| M2.2 | 第2周末 | 用户管理功能完成 |
| M2.3 | 第3周末 | 角色权限功能完成 |
| M2.4 | 第4周末 | 系统完善发布v1.0 |
## 4. 质量保障
### 4.1 测试策略
- **单元测试**:覆盖率 > 80%
- **集成测试**:关键业务流程测试
- **性能测试**并发用户1000+
- **安全测试**OWASP Top 10检查
### 4.2 代码质量
- **代码规范**ESLint + Prettier
- **类型安全**TypeScript严格模式
- **代码审查**Pull Request必须审查
- **文档完善**:接口文档、部署文档
### 4.3 监控告警
- **API监控**:响应时间、错误率
- **数据库监控**:连接数、慢查询
- **缓存监控**Redis状态监控
- **业务监控**:用户注册、登录统计
## 5. 风险评估
### 5.1 技术风险
| 风险 | 概率 | 影响 | 应对策略 |
|------|------|------|----------|
| 数据库性能瓶颈 | 中 | 高 | 索引优化、查询优化 |
| JWT安全性问题 | 低 | 高 | RefreshToken轮转、黑名单机制 |
| 并发数据一致性 | 中 | 中 | 乐观锁、Redis锁 |
### 5.2 进度风险
| 风险 | 概率 | 影响 | 应对策略 |
|------|------|------|----------|
| 需求变更 | 中 | 中 | 敏捷开发、版本控制 |
| 技术难点 | 低 | 中 | 技术预研、备选方案 |
| 测试时间不足 | 中 | 高 | 并行开发测试、自动化测试 |
## 6. 成功标准
### 6.1 功能指标
- ✅ 用户注册成功率 > 95%
- ✅ 登录响应时间 < 200ms
- ✅ 权限验证准确率 = 100%
- ✅ 密码安全强度符合规范
### 6.2 技术指标
- ✅ API可用性 > 99.9%
- ✅ 单元测试覆盖率 > 80%
- ✅ 代码质量评分 > 8.0
- ✅ 安全漏洞 = 0
### 6.3 用户体验指标
- ✅ 注册流程步骤 ≤ 3步
- ✅ 忘记密码找回成功率 > 90%
- ✅ 用户反馈满意度 > 85%
## 7. 后续规划
### 7.1 功能扩展
- OAuth第三方登录微信、GitHub等
- 两步验证2FA
- 单点登录SSO
- 用户行为分析
### 7.2 性能优化
- 分库分表策略
- 缓存策略优化
- CDN接入
- 数据库读写分离
### 7.3 运维支持
- 容器化部署
- 监控体系完善
- 自动化运维
- 灾备方案

View File

@ -0,0 +1,517 @@
# M2 - 基础用户系统 - 开发任务计划
## 📋 任务概览
基于接口设计文档和开发PRD本计划将M2基础用户系统开发分为4个阶段共70+个详细子任务预计4周完成。
### 🎯 总体目标
- 完成70+个详细开发子任务
- 建立完整的RBAC权限体系
- 实现安全的用户认证系统
- 构建可扩展的系统基础架构
### 📊 任务统计
- **总任务数**70+ 个子任务
- **P0核心任务**35个 (必须完成)
- **P1重要任务**25个 (优先完成)
- **P2优化任务**10个 (时间允许时完成)
## 📅 第一阶段:数据库设计与基础服务 (第1周5天)
### 🗄️ 数据库表结构设计 (第1-2天)
**预估工时**16小时
#### 核心任务:
- [x] **创建用户表 sys_users** `(4h)`
- 设计字段、索引、约束
- 支持软删除、乐观锁
- [x] **创建角色表 sys_roles** `(3h)`
- 支持树形结构、权限快照
- [x] **创建权限表 sys_permissions** `(3h)`
- 支持层级权限、资源权限
- [x] **创建关联表** `(3h)`
- user_roles、role_permissions、user_organizations等
- [x] **创建系统表** `(2h)`
- 字典表、标签表、日志表
- [x] **编写数据库迁移脚本和基础数据** `(1h)`
### 🔧 基础服务配置 (第3天)
**预估工时**8小时
#### 核心任务:
- [x] **完善数据库连接配置** `(2h)`
- 连接池、事务管理
- [x] **优化Redis配置** `(2h)`
- 缓存策略、session管理
- [x] **完善JWT配置** `(2h)`
- 密钥管理、过期时间、RefreshToken
- [x] **集成邮件发送服务** `(1h)`
- SMTP配置、邮件模板
- [x] **集成验证码服务** `(1h)`
- 图形验证码生成、验证
### 🔐 用户注册功能 (第4天)
**预估工时**8小时
#### 详细子任务:
- [ ] **实现注册参数验证** `(1h)`
- 用户名格式、邮箱格式、密码强度
- [ ] **实现唯一性检查** `(1h)`
- 用户名唯一、邮箱唯一
- [ ] **实现密码加密** `(1h)`
- bcrypt加密、盐值生成
- [ ] **实现激活机制** `(2h)`
- 生成激活令牌、Redis存储
- [ ] **实现注册邮件发送** `(2h)`
- 激活邮件模板、异步发送
- [ ] **编写注册功能测试** `(1h)`
### 🎫 用户激活与登录功能 (第5天)
**预估工时**8小时
#### 激活功能子任务:
- [ ] **实现激活令牌验证** `(1h)`
- Redis查询、过期检查
- [ ] **实现用户状态更新** `(1h)`
- 激活状态、数据库更新
- [ ] **发送欢迎邮件** `(1h)`
- 欢迎邮件模板、用户引导
- [ ] **编写激活功能测试** `(1h)`
#### 登录功能子任务:
- [ ] **实现多种登录方式** `(1h)`
- 用户名登录、邮箱登录
- [ ] **实现密码验证** `(1h)`
- bcrypt验证、用户状态检查
- [ ] **实现JWT生成** `(1h)`
- AccessToken、RefreshToken生成
- [ ] **实现登录失败限制** `(1h)`
- 失败次数统计、账号锁定
## 📅 第二阶段:用户管理与安全功能 (第2周5天)
### 🛡️ JWT认证中间件 (第6天)
**预估工时**8小时
#### 详细子任务:
- [ ] **实现JWT令牌验证** `(2h)`
- 签名验证、过期检查
- [ ] **实现用户信息提取** `(2h)`
- 从Token解析用户ID、角色
- [ ] **实现Token黑名单** `(2h)`
- Redis黑名单、退出登录处理
- [ ] **开发JWT中间件** `(2h)`
- 请求拦截、权限注入
### 🔄 Token管理功能 (第7天)
**预估工时**8小时
#### 详细子任务:
- [ ] **实现RefreshToken验证** `(2h)`
- Redis验证、用户状态检查
- [ ] **实现新Token生成** `(2h)`
- AccessToken刷新、RefreshToken轮转
- [ ] **实现Token撤销** `(2h)`
- 加入黑名单、清除RefreshToken
- [ ] **实现缓存清除** `(2h)`
- 用户缓存、权限缓存清理
### 👤 用户信息管理 (第8天)
**预估工时**8小时
#### 详细子任务:
- [ ] **实现基本信息查询** `(2h)`
- 用户基本信息、状态信息
- [ ] **实现角色信息查询** `(2h)`
- 用户角色、权限信息
- [ ] **实现组织信息查询** `(1h)`
- 用户组织、职位信息
- [ ] **实现用户信息缓存** `(2h)`
- Redis缓存、缓存更新策略
- [ ] **编写用户信息API测试** `(1h)`
### 📋 用户列表与CRUD (第9天)
**预估工时**8小时
#### 详细子任务:
- [ ] **实现分页查询** `(2h)`
- 页码参数、分页算法、数据返回格式
- [ ] **实现多条件筛选** `(2h)`
- 状态筛选、角色筛选、时间范围
- [ ] **实现关键词搜索** `(1h)`
- 用户名、邮箱、手机号、昵称搜索
- [ ] **实现排序功能** `(1h)`
- 创建时间、最后登录、用户名排序
- [ ] **实现用户CRUD操作** `(2h)`
- 创建用户、更新信息、软删除
### 🔒 密码管理功能 (第10天)
**预估工时**8小时
#### 密码修改子任务:
- [ ] **实现原密码验证** `(1h)`
- 当前密码检查、权限验证
- [ ] **实现新密码规则** `(1h)`
- 密码强度、确认密码检查
- [ ] **实现密码更新** `(1h)`
- bcrypt加密、数据库更新
- [ ] **实现密码修改后处理** `(1h)`
- 清除所有Token、发送通知
#### 密码重置子任务:
- [ ] **实现重置申请邮箱验证** `(1h)`
- 邮箱存在性、用户状态检查
- [ ] **实现验证码验证** `(1h)`
- 图形验证码检查、防刷机制
- [ ] **实现重置令牌生成** `(1h)`
- 6位数字码、Redis存储、过期时间
- [ ] **实现重置邮件发送** `(1h)`
- 邮件模板、发送频率限制
## 📅 第三阶段:角色权限系统 (第3周5天)
### 🏷️ 角色管理功能 (第11-12天)
**预估工时**16小时
#### 角色基础功能:
- [ ] **实现角色树形结构** `(3h)`
- 父子关系、层级路径、深度计算
- [ ] **实现角色基本操作** `(3h)`
- 创建、更新、删除、查询
- [ ] **实现角色权限分配** `(4h)`
- 权限选择、继承检查
- [ ] **实现权限快照** `(3h)`
- JSON存储、快照更新策略
- [ ] **实现权限继承** `(3h)`
- 上下级权限合并、继承规则
### 🔐 权限验证中间件 (第13-14天)
**预估工时**16小时
#### 权限验证核心:
- [ ] **实现认证检查** `(2h)`
- JWT验证、用户状态检查
- [ ] **实现RBAC权限检查** `(4h)`
- 角色权限查询、权限匹配
- [ ] **实现权限缓存** `(3h)`
- Redis缓存用户权限、缓存刷新
- [ ] **实现数据权限过滤** `(4h)`
- 基于角色的数据范围控制
- [ ] **实现用户角色分配** `(3h)`
- 角色添加、移除、有效期设置
### 🧪 权限系统测试 (第15天)
**预估工时**8小时
#### 测试任务:
- [ ] **权限验证准确性测试** `(3h)`
- [ ] **角色继承测试** `(2h)`
- [ ] **权限缓存测试** `(2h)`
- [ ] **性能压力测试** `(1h)`
## 📅 第四阶段:系统完善与优化 (第4周5天)
### 🏢 组织架构模块 (第16天)
**预估工时**8小时
#### 详细子任务:
- [ ] **实现组织树形结构** `(2h)`
- 父子关系、层级路径、深度管理
- [ ] **实现组织CRUD** `(2h)`
- 创建、更新、删除、查询组织
- [ ] **实现用户组织关系** `(2h)`
- 用户加入、离开、职位设置
- [ ] **实现组织权限** `(2h)`
- 组织级数据权限、层级权限
### 📚 字典标签系统 (第17天)
**预估工时**8小时
#### 字典管理:
- [ ] **实现字典类型管理** `(2h)`
- 字典类型CRUD、系统字典保护
- [ ] **实现字典项管理** `(2h)`
- 字典项CRUD、排序、状态管理
- [ ] **实现字典缓存策略** `(2h)`
- Redis缓存、懒加载、缓存刷新
- [ ] **实现字典前端API** `(2h)`
- 字典数据接口、国际化支持
### 🏷️ 标签系统 (第18天)
**预估工时**8小时
#### 详细子任务:
- [ ] **实现标签CRUD** `(2h)`
- 标签创建、更新、删除、查询
- [ ] **实现标签分类** `(2h)`
- 标签类型、颜色管理、分组显示
- [ ] **实现标签使用统计** `(2h)`
- 使用次数统计、热门标签排行
- [ ] **实现标签推荐** `(2h)`
- 基于使用频率的标签推荐算法
### 📋 日志审计系统 (第19天)
**预估工时**8小时
#### 详细子任务:
- [ ] **实现操作日志中间件** `(2h)`
- 自动记录用户操作、请求参数
- [ ] **实现操作日志查询** `(2h)`
- 日志列表、筛选、搜索功能
- [ ] **实现日志统计分析** `(2h)`
- 操作频率、用户行为分析
- [ ] **实现安全监控** `(2h)`
- 敏感操作监控、异常行为告警
### 🚀 性能优化与部署 (第20天)
**预估工时**8小时
#### 详细子任务:
- [ ] **数据库性能优化** `(2h)`
- 索引优化、查询优化、慢查询分析
- [ ] **缓存策略优化** `(2h)`
- 缓存命中率优化、缓存更新策略
- [ ] **API响应优化** `(2h)`
- 响应时间优化、并发处理优化
- [ ] **部署准备** `(2h)`
- 生产环境配置、监控配置、文档完善
## 📊 详细任务优先级分类
### 🔴 P0 - 核心功能 (必须完成35个任务)
**数据库基础**
- 用户表、角色表、权限表、关联表创建
- 基础服务配置
**认证核心**
- 用户注册、激活、登录
- JWT认证中间件
- Token管理
**用户管理**
- 用户信息管理、列表查询
- 基本CRUD操作
**权限控制**
- 角色管理、权限分配
- 权限验证中间件
### 🟡 P1 - 重要功能 (优先完成25个任务)
**安全功能**
- 密码重置、验证码系统
- 登录失败限制
**角色权限**
- 权限继承、权限缓存
- 用户角色管理
**系统管理**
- 组织架构、字典管理
- 标签系统
### 🟢 P2 - 优化功能 (时间允许时完成10个任务)
**增强功能**
- 操作日志、安全监控
- 性能优化
- 高级搜索功能
## 🔗 任务依赖关系图
```mermaid
graph TD
A[数据库表设计] --> B[基础服务配置]
B --> C[用户注册功能]
B --> D[验证码系统]
C --> E[用户激活功能]
D --> F[用户登录功能]
F --> G[JWT认证中间件]
G --> H[Token管理]
G --> I[用户信息管理]
I --> J[用户列表查询]
J --> K[角色管理]
K --> L[权限验证中间件]
L --> M[组织管理]
M --> N[字典标签系统]
N --> O[日志审计]
O --> P[性能优化]
```
## 📈 详细质量标准
### 代码质量指标
- [ ] **TypeScript严格模式**无any类型严格类型检查
- [ ] **ESLint检查**0警告代码风格统一
- [ ] **单元测试覆盖率**> 80%核心业务逻辑100%
- [ ] **集成测试**:完整业务流程端到端测试
### 性能质量指标
- [ ] **API响应时间**P95 < 200msP99 < 500ms
- [ ] **数据库查询**无N+1问题慢查询 < 100ms
- [ ] **Redis缓存**:命中率 > 90%,内存使用合理
- [ ] **并发支持**1000+并发用户,无性能退化
### 安全质量指标
- [ ] **密码安全**bcrypt成本因子12强密码策略
- [ ] **JWT安全**:安全密钥,合理过期时间
- [ ] **输入验证**:所有输入严格校验,防止注入攻击
- [ ] **权限控制**RBAC模型正确实现无权限绕过
## 🚨 详细风险控制
### 技术风险及应对
| 风险项 | 风险等级 | 影响 | 应对策略 |
|--------|----------|------|----------|
| 数据库性能瓶颈 | 中 | 高 | 提前压力测试,索引优化,读写分离准备 |
| JWT安全性问题 | 低 | 高 | RefreshToken轮转黑名单机制安全审计 |
| 缓存一致性问题 | 中 | 中 | 设计缓存更新策略,版本控制,监控机制 |
| 权限设计复杂度 | 中 | 中 | 分阶段实现,充分测试,文档完善 |
### 进度风险及应对
| 风险项 | 风险等级 | 影响 | 应对策略 |
|--------|----------|------|----------|
| 需求变更 | 中 | 中 | 敏捷开发,版本控制,需求冻结期 |
| 技术难点 | 低 | 中 | 技术预研,备选方案,专家咨询 |
| 测试时间不足 | 中 | 高 | 并行开发测试,自动化测试,持续集成 |
| 第三方服务依赖 | 低 | 中 | 本地Mock降级方案多供应商备选 |
## 📋 阶段性验收标准
### 第一阶段验收 (第1周末)
**功能验收**
- [ ] 用户注册流程完整可用
- [ ] 邮箱激活功能正常
- [ ] 用户登录功能稳定
- [ ] 基础数据库表结构完善
**质量验收**
- [ ] 单元测试覆盖率 > 70%
- [ ] 接口响应时间 < 300ms
- [ ] 无明显安全漏洞
### 第二阶段验收 (第2周末)
**功能验收**
- [ ] 用户管理功能完整
- [ ] 密码管理功能可用
- [ ] JWT认证机制稳定
- [ ] 基础权限控制实现
**质量验收**
- [ ] 单元测试覆盖率 > 75%
- [ ] 集成测试覆盖主要流程
- [ ] 接口文档完整准确
### 第三阶段验收 (第3周末)
**功能验收**
- [ ] 完整RBAC权限体系
- [ ] 角色管理功能完善
- [ ] 权限验证准确无误
- [ ] 数据权限控制有效
**质量验收**
- [ ] 单元测试覆盖率 > 80%
- [ ] 权限测试100%通过
- [ ] 性能测试达标
### 第四阶段验收 (第4周末)
**功能验收**
- [ ] 系统功能完整
- [ ] 组织架构可用
- [ ] 字典标签系统完善
- [ ] 日志审计功能正常
**质量验收**
- [ ] 所有测试用例通过
- [ ] 生产环境部署成功
- [ ] 监控告警正常
- [ ] 文档完整可用
## 📅 每日工作计划建议
### 典型工作日安排 (8小时)
- **09:00-10:30**:核心开发任务 (1.5h)
- **10:30-10:45**:休息
- **10:45-12:00**:核心开发任务 (1.25h)
- **12:00-13:00**:午餐休息
- **13:00-14:30**:核心开发任务 (1.5h)
- **14:30-14:45**:休息
- **14:45-16:15**:开发任务/代码审查 (1.5h)
- **16:15-16:30**:休息
- **16:30-17:30**:测试编写/文档更新 (1h)
- **17:30-18:00**:每日总结/明日规划 (0.5h)
### 关键节点提醒
- **每日**更新TODO进度提交代码
- **每周五**:阶段性总结,风险评估
- **里程碑**:功能验收,质量检查
- **每2周**:技术债务清理,重构优化
---
**预计总工作量**160工时 (4周 × 40工时/周)
**项目优先级**P0 (最高优先级)
**团队规模建议**1-2人
**技术难度评级**:中等偏上
**成功概率评估**85% (基于当前技术栈和时间安排)