From 0a74b7fb35b0103d6c96135ab4a4a0e69019f6ff Mon Sep 17 00:00:00 2001 From: "HeXiaoLong:Suanier" Date: Fri, 4 Jul 2025 18:17:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E9=82=AE=E4=BB=B6?= =?UTF-8?q?=E5=8F=91=E9=80=81=E6=9C=8D=E5=8A=A1=E9=9B=86=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现完整的邮件发送服务类,支持重试机制和错误处理 - 集成5种邮件模板(激活、密码重置、欢迎、通知、密码修改) - 创建Elysia邮件服务插件,支持便捷方法调用 - 添加完整的TypeScript类型定义和配置文件 - 编写全面的测试用例,覆盖所有功能 - 支持SMTP连接池、健康检查和状态监控 关联M2基础用户系统开发任务 --- .../rules/global-rules/process-list-task.mdc | 9 - bun.lock | 779 ++++++++++++++++++ package.json | 2 + src/config/email.config.ts | 128 +++ src/config/index.ts | 1 + src/plugins/email/README.md | 217 +++++ src/plugins/email/email.plugins.ts | 242 ++++++ src/plugins/email/email.service.ts | 610 ++++++++++++++ src/plugins/index.ts | 3 + src/tests/email.test.ts | 361 ++++++++ src/type/email.type.ts | 255 ++++++ tasks/M2-基础用户系统-开发PRD.md | 234 ++++++ tasks/M2-基础用户系统-开发任务计划.md | 517 ++++++++++++ 13 files changed, 3349 insertions(+), 9 deletions(-) create mode 100644 bun.lock create mode 100644 src/config/email.config.ts create mode 100644 src/plugins/email/README.md create mode 100644 src/plugins/email/email.plugins.ts create mode 100644 src/plugins/email/email.service.ts create mode 100644 src/tests/email.test.ts create mode 100644 src/type/email.type.ts create mode 100644 tasks/M2-基础用户系统-开发PRD.md create mode 100644 tasks/M2-基础用户系统-开发任务计划.md diff --git a/.cursor/rules/global-rules/process-list-task.mdc b/.cursor/rules/global-rules/process-list-task.mdc index 80713f0..5017c9e 100644 --- a/.cursor/rules/global-rules/process-list-task.mdc +++ b/.cursor/rules/global-rules/process-list-task.mdc @@ -1,15 +1,6 @@ --- -description: -globs: alwaysApply: true --- - ---- - -**文档2:任务清单管理 (Task List Management)** - -```markdown ---- 描述 (description): 全局模式 (globs): 始终应用 (alwaysApply): false diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..803d320 --- /dev/null +++ b/bun.lock @@ -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=="], + } +} diff --git a/package.json b/package.json index 145470e..8769eed 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "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", @@ -34,6 +35,7 @@ "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", diff --git a/src/config/email.config.ts b/src/config/email.config.ts new file mode 100644 index 0000000..876c0c5 --- /dev/null +++ b/src/config/email.config.ts @@ -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, +}); \ No newline at end of file diff --git a/src/config/index.ts b/src/config/index.ts index 6829bc8..4cc85ff 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,4 +1,5 @@ export * from './db.config'; +export * from './email.config'; export * from './jwt.config'; export * from './logger.config'; export * from './redis.config'; diff --git a/src/plugins/email/README.md b/src/plugins/email/README.md new file mode 100644 index 0000000..a45fbe6 --- /dev/null +++ b/src/plugins/email/README.md @@ -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="星撰系统 " +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: '

Hello World!

', + 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. **安全性**: 避免在邮件内容中包含敏感信息 \ No newline at end of file diff --git a/src/plugins/email/email.plugins.ts b/src/plugins/email/email.plugins.ts new file mode 100644 index 0000000..0d61a80 --- /dev/null +++ b/src/plugins/email/email.plugins.ts @@ -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 { + return await emailService.sendEmail(options); + }, + + /** + * 发送模板邮件 + */ + async sendTemplateEmail(options: EmailTemplateSendOptions): Promise { + return await emailService.sendTemplateEmail(options); + }, + + /** + * 获取邮件服务状态 + */ + getStatus(): EmailServiceStatus { + return emailService.status; + }, + + /** + * 执行健康检查 + */ + async healthCheck(): Promise { + 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 { + 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 { + 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 { + 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 { + 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 { + 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; + sendTemplateEmail(options: EmailTemplateSendOptions): Promise; + getStatus(): EmailServiceStatus; + healthCheck(): Promise; + isInitialized(): boolean; + sendActivationEmail(options: { + to: string; + username: string; + nickname?: string; + activationUrl: string; + activationCode: string; + expireTime: string; + }): Promise; + sendPasswordResetEmail(options: { + to: string; + username: string; + nickname?: string; + resetUrl: string; + resetCode: string; + expireTime: string; + }): Promise; + sendWelcomeEmail(options: { + to: string; + username: string; + nickname?: string; + }): Promise; + sendPasswordChangedEmail(options: { + to: string; + username: string; + nickname?: string; + }): Promise; + sendNotificationEmail(options: { + to: string | string[]; + username?: string; + nickname?: string; + message: string; + subject?: string; + }): Promise; + }; +}; \ No newline at end of file diff --git a/src/plugins/email/email.service.ts b/src/plugins/email/email.service.ts new file mode 100644 index 0000000..8b35de3 --- /dev/null +++ b/src/plugins/email/email.service.ts @@ -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 { + // 防止重复初始化 + 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 { + 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 { + 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 ` + + + + + 激活您的账户 + + +
+

激活您的账户

+

您好 ${params.nickname || params.username},

+

感谢您注册 ${params.systemName}!请点击下面的按钮激活您的账户:

+ +

或者您可以复制以下链接到浏览器地址栏:

+

${params.activationUrl}

+

激活码:${params.activationCode}

+

注意:此链接将在 ${params.expireTime} 后过期。

+

如果您没有注册账户,请忽略此邮件。

+
+

+ 此邮件由 ${params.systemName} 自动发送,请勿回复。
+ 如有疑问,请联系:${params.supportEmail} +

+
+ +`; + } + + /** + * 获取密码重置邮件模板 + */ + private getPasswordResetTemplate(params: EmailTemplateParams): string { + return ` + + + + + 重置您的密码 + + +
+

重置您的密码

+

您好 ${params.nickname || params.username},

+

我们收到了重置您账户密码的请求。请点击下面的按钮重置密码:

+ +

或者您可以复制以下链接到浏览器地址栏:

+

${params.resetUrl}

+

重置码:${params.resetCode}

+

注意:此链接将在 ${params.expireTime} 后过期。

+

如果您没有请求重置密码,请忽略此邮件,您的密码不会被更改。

+
+

+ 此邮件由 ${params.systemName} 自动发送,请勿回复。
+ 如有疑问,请联系:${params.supportEmail} +

+
+ +`; + } + + /** + * 获取欢迎邮件模板 + */ + private getWelcomeTemplate(params: EmailTemplateParams): string { + return ` + + + + + 欢迎加入 + + +
+

欢迎加入 ${params.systemName}!

+

您好 ${params.nickname || params.username},

+

感谢您注册 ${params.systemName}!您的账户已成功激活,现在可以开始使用我们的服务了。

+
+

接下来您可以:

+
    +
  • 完善您的个人资料
  • +
  • 探索系统功能
  • +
  • 联系我们的客服团队
  • +
+
+

如果您在使用过程中遇到任何问题,请随时联系我们。

+
+

+ 此邮件由 ${params.systemName} 自动发送,请勿回复。
+ 如有疑问,请联系:${params.supportEmail} +

+
+ +`; + } + + /** + * 获取密码修改通知模板 + */ + private getPasswordChangedTemplate(params: EmailTemplateParams): string { + return ` + + + + + 密码已修改 + + +
+

密码修改通知

+

您好 ${params.nickname || params.username},

+

您的账户密码已成功修改。

+
+

修改时间: ${new Date().toLocaleString('zh-CN')}

+
+

如果这不是您本人的操作,请立即联系我们并采取以下措施:

+
    +
  • 立即联系客服:${params.supportEmail}
  • +
  • 检查账户安全设置
  • +
  • 更改相关联的其他账户密码
  • +
+
+

+ 此邮件由 ${params.systemName} 自动发送,请勿回复。
+ 如有疑问,请联系:${params.supportEmail} +

+
+ +`; + } + + /** + * 获取通知邮件模板 + */ + private getNotificationTemplate(params: EmailTemplateParams): string { + const message = (params as any).message || '您有新的系统通知,请登录查看详情。'; + return ` + + + + + 系统通知 + + +
+

系统通知

+

您好 ${params.nickname || params.username},

+
+ ${message} +
+
+

+ 此邮件由 ${params.systemName} 自动发送,请勿回复。
+ 如有疑问,请联系:${params.supportEmail} +

+
+ +`; + } + + /** + * 执行健康检查 + */ + public async healthCheck(): Promise { + 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 { + 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 }; \ No newline at end of file diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 5ec5483..354f7ac 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -13,6 +13,7 @@ import { errorHandlerPlugin } from '@/plugins/errorHandle/errorHandler.plugins'; import { swaggerPlugin } from '@/plugins/swagger/swagger.plugins'; import { drizzlePlugin } from '@/plugins/drizzle/drizzle.plugins'; import { redisPlugin } from '@/plugins/redis/redis.plugins'; +import { emailPlugin } from '@/plugins/email/email.plugins'; export const plugins = (app: Elysia) => app @@ -24,5 +25,7 @@ export const plugins = (app: Elysia) => .use(drizzlePlugin) // Redis插件 .use(redisPlugin) + // 邮件服务插件 + .use(emailPlugin) // API 文档插件 .use(swaggerPlugin); diff --git a/src/tests/email.test.ts b/src/tests/email.test.ts new file mode 100644 index 0000000..3ccdac2 --- /dev/null +++ b/src/tests/email.test.ts @@ -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: '

这是一封测试邮件的HTML内容

', + }; + + 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: '

测试抄送功能

这封邮件测试抄送功能。

', + }; + + 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('模板渲染失败'); + } + }); + }); +}); \ No newline at end of file diff --git a/src/type/email.type.ts b/src/type/email.type.ts new file mode 100644 index 0000000..b9e092e --- /dev/null +++ b/src/type/email.type.ts @@ -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; + /** 消息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; +} \ No newline at end of file diff --git a/tasks/M2-基础用户系统-开发PRD.md b/tasks/M2-基础用户系统-开发PRD.md new file mode 100644 index 0000000..e407d72 --- /dev/null +++ b/tasks/M2-基础用户系统-开发PRD.md @@ -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 运维支持 +- 容器化部署 +- 监控体系完善 +- 自动化运维 +- 灾备方案 \ No newline at end of file diff --git a/tasks/M2-基础用户系统-开发任务计划.md b/tasks/M2-基础用户系统-开发任务计划.md new file mode 100644 index 0000000..fcded13 --- /dev/null +++ b/tasks/M2-基础用户系统-开发任务计划.md @@ -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 < 200ms,P99 < 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% (基于当前技术栈和时间安排) \ No newline at end of file