From 0b353ad6a0f3ce4825d7e3131aae9435950e25e0 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 12:23:11 +0800 Subject: [PATCH 01/22] chore(lint): configure eslint prettier and lint-staged --- .gitignore | 1 + .lintstagedrc.cjs | 4 + .prettierignore | 7 + .prettierrc.json | 6 + eslint.config.mjs | 29 ++ package.json | 10 +- pnpm-lock.yaml | 799 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 854 insertions(+), 2 deletions(-) create mode 100644 .lintstagedrc.cjs create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 eslint.config.mjs diff --git a/.gitignore b/.gitignore index 5074169..ee6d817 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ develop.md node_modules/ .turbo/ .idea/ +.eslintcache diff --git a/.lintstagedrc.cjs b/.lintstagedrc.cjs new file mode 100644 index 0000000..b7ddb53 --- /dev/null +++ b/.lintstagedrc.cjs @@ -0,0 +1,4 @@ +module.exports = { + "*.{js,mjs,cjs,ts,tsx}": ["eslint --fix", "prettier --write"], + "*.{json,md,yml,yaml}": ["prettier --write"] +}; diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1613f18 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +node_modules +.turbo +.idea +dist +build +coverage +*.png diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..8a0f27e --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": false, + "trailingComma": "none", + "printWidth": 100 +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..665e53b --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,29 @@ +import js from "@eslint/js"; +import globals from "globals"; + +export default [ + { + ignores: ["**/node_modules/**", "**/.turbo/**", "**/dist/**", "**/build/**"] + }, + js.configs.recommended, + { + files: ["**/*.{js,mjs}"], + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + globals: { + ...globals.node + } + } + }, + { + files: ["**/*.cjs"], + languageOptions: { + ecmaVersion: "latest", + sourceType: "commonjs", + globals: { + ...globals.node + } + } + } +]; diff --git a/package.json b/package.json index 58df56e..a1ae063 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,10 @@ "test": "turbo run test", "dev": "turbo run dev --parallel", "build": "turbo run build", - "lint": "turbo run lint", + "lint": "turbo run lint && eslint .", "typecheck": "turbo run typecheck", - "format": "turbo run format" + "format": "prettier --write .", + "lint:staged": "lint-staged" }, "keywords": [], "author": "", @@ -17,6 +18,11 @@ "private": true, "packageManager": "pnpm@9.15.2", "devDependencies": { + "@eslint/js": "^10.0.1", + "eslint": "^10.2.0", + "globals": "^17.4.0", + "lint-staged": "^16.4.0", + "prettier": "^3.8.1", "turbo": "^2.9.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 00cebb6..3310851 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,21 @@ importers: .: devDependencies: + '@eslint/js': + specifier: ^10.0.1 + version: 10.0.1(eslint@10.2.0) + eslint: + specifier: ^10.2.0 + version: 10.2.0 + globals: + specifier: ^17.4.0 + version: 17.4.0 + lint-staged: + specifier: ^16.4.0 + version: 16.4.0 + prettier: + specifier: ^3.8.1 + version: 3.8.1 turbo: specifier: ^2.9.3 version: 2.9.3 @@ -18,6 +33,61 @@ importers: packages: + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.23.4': + resolution: {integrity: sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/config-helpers@0.5.4': + resolution: {integrity: sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.2.0': + resolution: {integrity: sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/js@10.0.1': + resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/object-schema@3.0.4': + resolution: {integrity: sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.7.0': + resolution: {integrity: sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + '@turbo/darwin-64@2.9.3': resolution: {integrity: sha512-P8foouaP+y/p+hhEGBoZpzMbpVvUMwPjDpcy6wN7EYfvvyISD1USuV27qWkczecihwuPJzQ1lDBuL8ERcavTyg==} cpu: [x64] @@ -48,12 +118,409 @@ packages: cpu: [arm64] os: [win32] + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + engines: {node: 18 || 20 || >=22} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.2.0: + resolution: {integrity: sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + get-east-asian-width@1.5.0: + resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} + engines: {node: '>=18'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@17.4.0: + resolution: {integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==} + engines: {node: '>=18'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lint-staged@16.4.0: + resolution: {integrity: sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==} + engines: {node: '>=20.17'} + hasBin: true + + listr2@9.0.5: + resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} + engines: {node: '>=20.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + engines: {node: '>=14'} + hasBin: true + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.2.0: + resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} + engines: {node: '>=20'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + tinyexec@1.0.4: + resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + engines: {node: '>=18'} + turbo@2.9.3: resolution: {integrity: sha512-J/VUvsGRykPb9R8Kh8dHVBOqioDexLk9BhLCU/ZybRR+HN9UR3cURdazFvNgMDt9zPP8TF6K73Z+tplfmi0PqQ==} hasBin: true + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + engines: {node: '>= 14.6'} + hasBin: true + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + snapshots: + '@eslint-community/eslint-utils@4.9.1(eslint@10.2.0)': + dependencies: + eslint: 10.2.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.4': + dependencies: + '@eslint/object-schema': 3.0.4 + debug: 4.4.3 + minimatch: 10.2.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.5.4': + dependencies: + '@eslint/core': 1.2.0 + + '@eslint/core@1.2.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/js@10.0.1(eslint@10.2.0)': + optionalDependencies: + eslint: 10.2.0 + + '@eslint/object-schema@3.0.4': {} + + '@eslint/plugin-kit@0.7.0': + dependencies: + '@eslint/core': 1.2.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + '@turbo/darwin-64@2.9.3': optional: true @@ -72,6 +539,314 @@ snapshots: '@turbo/windows-arm64@2.9.3': optional: true + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + + ansi-regex@6.2.2: {} + + ansi-styles@6.2.3: {} + + balanced-match@4.0.4: {} + + brace-expansion@5.0.5: + dependencies: + balanced-match: 4.0.4 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@5.2.0: + dependencies: + slice-ansi: 8.0.0 + string-width: 8.2.0 + + colorette@2.0.20: {} + + commander@14.0.3: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + emoji-regex@10.6.0: {} + + environment@1.1.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.8 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.2.0: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.4 + '@eslint/config-helpers': 0.5.4 + '@eslint/core': 1.2.0 + '@eslint/plugin-kit': 0.7.0 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.14.0 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@11.2.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + eventemitter3@5.0.4: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + get-east-asian-width@1.5.0: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@17.4.0: {} + + ignore@5.3.2: {} + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.5.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + isexe@2.0.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lint-staged@16.4.0: + dependencies: + commander: 14.0.3 + listr2: 9.0.5 + picomatch: 4.0.4 + string-argv: 0.3.2 + tinyexec: 1.0.4 + yaml: 2.8.3 + + listr2@9.0.5: + dependencies: + cli-truncate: 5.2.0 + colorette: 2.0.20 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + mimic-function@5.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.5 + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picomatch@4.0.4: {} + + prelude-ls@1.2.1: {} + + prettier@3.8.1: {} + + punycode@2.3.1: {} + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + rfdc@1.4.1: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + string-argv@0.3.2: {} + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + string-width@8.2.0: + dependencies: + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + tinyexec@1.0.4: {} + turbo@2.9.3: optionalDependencies: '@turbo/darwin-64': 2.9.3 @@ -80,3 +855,27 @@ snapshots: '@turbo/linux-arm64': 2.9.3 '@turbo/windows-64': 2.9.3 '@turbo/windows-arm64': 2.9.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + yaml@2.8.3: {} + + yocto-queue@0.1.0: {} From 0ccb086598d803ac7715160ebfe5950538325bfc Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 12:25:35 +0800 Subject: [PATCH 02/22] chore(husky): add pre-commit and pre-push hooks --- .gitignore | 1 + .husky/pre-commit | 1 + .husky/pre-push | 2 + package.json | 4 +- pnpm-lock.yaml | 854 ++++++++++++++++++++++++++++++++-------------- 5 files changed, 601 insertions(+), 261 deletions(-) create mode 100755 .husky/pre-commit create mode 100755 .husky/pre-push diff --git a/.gitignore b/.gitignore index ee6d817..ce34796 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ .turbo/ .idea/ .eslintcache +/.husky/_ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..f9241ee --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpm lint:staged diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 0000000..4226722 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,2 @@ +pnpm typecheck +pnpm test diff --git a/package.json b/package.json index a1ae063..057bdd4 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "lint": "turbo run lint && eslint .", "typecheck": "turbo run typecheck", "format": "prettier --write .", - "lint:staged": "lint-staged" + "lint:staged": "lint-staged", + "prepare": "husky" }, "keywords": [], "author": "", @@ -21,6 +22,7 @@ "@eslint/js": "^10.0.1", "eslint": "^10.2.0", "globals": "^17.4.0", + "husky": "^9.1.7", "lint-staged": "^16.4.0", "prettier": "^3.8.1", "turbo": "^2.9.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3310851..3c014fa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,14 +1,13 @@ -lockfileVersion: '9.0' +lockfileVersion: "9.0" settings: autoInstallPeers: true excludeLinksFromLockfile: false importers: - .: devDependencies: - '@eslint/js': + "@eslint/js": specifier: ^10.0.1 version: 10.0.1(eslint@10.2.0) eslint: @@ -17,6 +16,9 @@ importers: globals: specifier: ^17.4.0 version: 17.4.0 + husky: + specifier: ^9.1.7 + version: 9.1.7 lint-staged: specifier: ^16.4.0 version: 16.4.0 @@ -32,518 +34,848 @@ importers: packages/tsconfig: {} packages: - - '@eslint-community/eslint-utils@4.9.1': - resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + "@eslint-community/eslint-utils@4.9.1": + resolution: + { + integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.2': - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + "@eslint-community/regexpp@4.12.2": + resolution: + { + integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== + } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } - '@eslint/config-array@0.23.4': - resolution: {integrity: sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} + "@eslint/config-array@0.23.4": + resolution: + { + integrity: sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow== + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - '@eslint/config-helpers@0.5.4': - resolution: {integrity: sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} + "@eslint/config-helpers@0.5.4": + resolution: + { + integrity: sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg== + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - '@eslint/core@1.2.0': - resolution: {integrity: sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} + "@eslint/core@1.2.0": + resolution: + { + integrity: sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A== + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - '@eslint/js@10.0.1': - resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} + "@eslint/js@10.0.1": + resolution: + { + integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA== + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } peerDependencies: eslint: ^10.0.0 peerDependenciesMeta: eslint: optional: true - '@eslint/object-schema@3.0.4': - resolution: {integrity: sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} + "@eslint/object-schema@3.0.4": + resolution: + { + integrity: sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw== + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - '@eslint/plugin-kit@0.7.0': - resolution: {integrity: sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} + "@eslint/plugin-kit@0.7.0": + resolution: + { + integrity: sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg== + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} + "@humanfs/core@0.19.1": + resolution: + { + integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + } + engines: { node: ">=18.18.0" } - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} - engines: {node: '>=18.18.0'} + "@humanfs/node@0.16.7": + resolution: + { + integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== + } + engines: { node: ">=18.18.0" } - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} + "@humanwhocodes/module-importer@1.0.1": + resolution: + { + integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + } + engines: { node: ">=12.22" } - '@humanwhocodes/retry@0.4.3': - resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} - engines: {node: '>=18.18'} + "@humanwhocodes/retry@0.4.3": + resolution: + { + integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== + } + engines: { node: ">=18.18" } - '@turbo/darwin-64@2.9.3': - resolution: {integrity: sha512-P8foouaP+y/p+hhEGBoZpzMbpVvUMwPjDpcy6wN7EYfvvyISD1USuV27qWkczecihwuPJzQ1lDBuL8ERcavTyg==} + "@turbo/darwin-64@2.9.3": + resolution: + { + integrity: sha512-P8foouaP+y/p+hhEGBoZpzMbpVvUMwPjDpcy6wN7EYfvvyISD1USuV27qWkczecihwuPJzQ1lDBuL8ERcavTyg== + } cpu: [x64] os: [darwin] - '@turbo/darwin-arm64@2.9.3': - resolution: {integrity: sha512-SIzEkvtNdzdI50FJDaIQ6kQGqgSSdFPcdn0wqmmONN6iGKjy6hsT+EH99GP65FsfV7DLZTh2NmtTIRl2kdoz5Q==} + "@turbo/darwin-arm64@2.9.3": + resolution: + { + integrity: sha512-SIzEkvtNdzdI50FJDaIQ6kQGqgSSdFPcdn0wqmmONN6iGKjy6hsT+EH99GP65FsfV7DLZTh2NmtTIRl2kdoz5Q== + } cpu: [arm64] os: [darwin] - '@turbo/linux-64@2.9.3': - resolution: {integrity: sha512-pLRwFmcHHNBvsCySLS6OFabr/07kDT2pxEt/k6eBf/3asiVQZKJ7Rk88AafQx2aYA641qek4RsXvYO3JYpiBug==} + "@turbo/linux-64@2.9.3": + resolution: + { + integrity: sha512-pLRwFmcHHNBvsCySLS6OFabr/07kDT2pxEt/k6eBf/3asiVQZKJ7Rk88AafQx2aYA641qek4RsXvYO3JYpiBug== + } cpu: [x64] os: [linux] - '@turbo/linux-arm64@2.9.3': - resolution: {integrity: sha512-gy6ApUroC2Nzv+qjGtE/uPNkhHAFU4c8God+zd5Aiv9L9uBgHlxVJpHT3XWl5xwlJZ2KWuMrlHTaS5kmNB+q1Q==} + "@turbo/linux-arm64@2.9.3": + resolution: + { + integrity: sha512-gy6ApUroC2Nzv+qjGtE/uPNkhHAFU4c8God+zd5Aiv9L9uBgHlxVJpHT3XWl5xwlJZ2KWuMrlHTaS5kmNB+q1Q== + } cpu: [arm64] os: [linux] - '@turbo/windows-64@2.9.3': - resolution: {integrity: sha512-d0YelTX6hAsB7kIEtGB3PzIzSfAg3yDoUlHwuwJc3adBXUsyUIs0YLG+1NNtuhcDOUGnWQeKUoJ2pGWvbpRj7w==} + "@turbo/windows-64@2.9.3": + resolution: + { + integrity: sha512-d0YelTX6hAsB7kIEtGB3PzIzSfAg3yDoUlHwuwJc3adBXUsyUIs0YLG+1NNtuhcDOUGnWQeKUoJ2pGWvbpRj7w== + } cpu: [x64] os: [win32] - '@turbo/windows-arm64@2.9.3': - resolution: {integrity: sha512-/08CwpKJl3oRY8nOlh2YgilZVJDHsr60XTNxRhuDeuFXONpUZ5X+Nv65izbG/xBew9qxcJFbDX9/sAmAX+ITcQ==} + "@turbo/windows-arm64@2.9.3": + resolution: + { + integrity: sha512-/08CwpKJl3oRY8nOlh2YgilZVJDHsr60XTNxRhuDeuFXONpUZ5X+Nv65izbG/xBew9qxcJFbDX9/sAmAX+ITcQ== + } cpu: [arm64] os: [win32] - '@types/esrecurse@4.3.1': - resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + "@types/esrecurse@4.3.1": + resolution: + { + integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw== + } - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + "@types/estree@1.0.8": + resolution: + { + integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + } - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + "@types/json-schema@7.0.15": + resolution: + { + integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + } acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + resolution: + { + integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + } peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} - engines: {node: '>=0.4.0'} + resolution: + { + integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + } + engines: { node: ">=0.4.0" } hasBin: true ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + resolution: + { + integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw== + } ansi-escapes@7.3.0: - resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg== + } + engines: { node: ">=18" } ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + } + engines: { node: ">=12" } ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + } + engines: { node: ">=12" } balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} - engines: {node: 18 || 20 || >=22} + resolution: + { + integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== + } + engines: { node: 18 || 20 || >=22 } brace-expansion@5.0.5: - resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} - engines: {node: 18 || 20 || >=22} + resolution: + { + integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ== + } + engines: { node: 18 || 20 || >=22 } cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== + } + engines: { node: ">=18" } cli-truncate@5.2.0: - resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw== + } + engines: { node: ">=20" } colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + resolution: + { + integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + } commander@14.0.3: - resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw== + } + engines: { node: ">=20" } cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} + resolution: + { + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + } + engines: { node: ">= 8" } debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} + resolution: + { + integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + } + engines: { node: ">=6.0" } peerDependencies: - supports-color: '*' + supports-color: "*" peerDependenciesMeta: supports-color: optional: true deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + resolution: + { + integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + } emoji-regex@10.6.0: - resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + resolution: + { + integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== + } environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== + } + engines: { node: ">=18" } escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + } + engines: { node: ">=10" } eslint-scope@9.1.2: - resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} + resolution: + { + integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ== + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + resolution: + { + integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } eslint-visitor-keys@5.0.1: - resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} + resolution: + { + integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } eslint@10.2.0: - resolution: {integrity: sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} + resolution: + { + integrity: sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA== + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } hasBin: true peerDependencies: - jiti: '*' + jiti: "*" peerDependenciesMeta: jiti: optional: true espree@11.2.0: - resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} + resolution: + { + integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw== + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } esquery@1.7.0: - resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} - engines: {node: '>=0.10'} + resolution: + { + integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== + } + engines: { node: ">=0.10" } esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} + resolution: + { + integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + } + engines: { node: ">=4.0" } estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} + resolution: + { + integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + } + engines: { node: ">=4.0" } esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + } + engines: { node: ">=0.10.0" } eventemitter3@5.0.4: - resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + resolution: + { + integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== + } fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + resolution: + { + integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + } fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + resolution: + { + integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + } fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + resolution: + { + integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + } file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} + resolution: + { + integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + } + engines: { node: ">=16.0.0" } find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + } + engines: { node: ">=10" } flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} + resolution: + { + integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + } + engines: { node: ">=16" } flatted@3.4.2: - resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + resolution: + { + integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== + } get-east-asian-width@1.5.0: - resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA== + } + engines: { node: ">=18" } glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} + resolution: + { + integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + } + engines: { node: ">=10.13.0" } globals@17.4.0: - resolution: {integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw== + } + engines: { node: ">=18" } + + husky@9.1.7: + resolution: + { + integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== + } + engines: { node: ">=18" } + hasBin: true ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} + resolution: + { + integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + } + engines: { node: ">= 4" } imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} + resolution: + { + integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + } + engines: { node: ">=0.8.19" } is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + } + engines: { node: ">=0.10.0" } is-fullwidth-code-point@5.1.0: - resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ== + } + engines: { node: ">=18" } is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + } + engines: { node: ">=0.10.0" } isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + resolution: + { + integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + } json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + resolution: + { + integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + } json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + resolution: + { + integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + } json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + resolution: + { + integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + } keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + resolution: + { + integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + } levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} + resolution: + { + integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + } + engines: { node: ">= 0.8.0" } lint-staged@16.4.0: - resolution: {integrity: sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==} - engines: {node: '>=20.17'} + resolution: + { + integrity: sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw== + } + engines: { node: ">=20.17" } hasBin: true listr2@9.0.5: - resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g== + } + engines: { node: ">=20.0.0" } locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + } + engines: { node: ">=10" } log-update@6.1.0: - resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== + } + engines: { node: ">=18" } mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== + } + engines: { node: ">=18" } minimatch@10.2.5: - resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} - engines: {node: 18 || 20 || >=22} + resolution: + { + integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== + } + engines: { node: 18 || 20 || >=22 } ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + } natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + resolution: + { + integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + } onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== + } + engines: { node: ">=18" } optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} + resolution: + { + integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + } + engines: { node: ">= 0.8.0" } p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + } + engines: { node: ">=10" } p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + } + engines: { node: ">=10" } path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + } + engines: { node: ">=8" } path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + } + engines: { node: ">=8" } picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== + } + engines: { node: ">=12" } prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} + resolution: + { + integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + } + engines: { node: ">= 0.8.0" } prettier@3.8.1: - resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== + } + engines: { node: ">=14" } hasBin: true punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + } + engines: { node: ">=6" } restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== + } + engines: { node: ">=18" } rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + resolution: + { + integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + } shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + } + engines: { node: ">=8" } shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + } + engines: { node: ">=8" } signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + } + engines: { node: ">=14" } slice-ansi@7.1.2: - resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w== + } + engines: { node: ">=18" } slice-ansi@8.0.0: - resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg== + } + engines: { node: ">=20" } string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} + resolution: + { + integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + } + engines: { node: ">=0.6.19" } string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + } + engines: { node: ">=18" } string-width@8.2.0: - resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw== + } + engines: { node: ">=20" } strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== + } + engines: { node: ">=12" } tinyexec@1.0.4: - resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw== + } + engines: { node: ">=18" } turbo@2.9.3: - resolution: {integrity: sha512-J/VUvsGRykPb9R8Kh8dHVBOqioDexLk9BhLCU/ZybRR+HN9UR3cURdazFvNgMDt9zPP8TF6K73Z+tplfmi0PqQ==} + resolution: + { + integrity: sha512-J/VUvsGRykPb9R8Kh8dHVBOqioDexLk9BhLCU/ZybRR+HN9UR3cURdazFvNgMDt9zPP8TF6K73Z+tplfmi0PqQ== + } hasBin: true type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} + resolution: + { + integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + } + engines: { node: ">= 0.8.0" } uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + resolution: + { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + } which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} + resolution: + { + integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + } + engines: { node: ">= 8" } hasBin: true word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + } + engines: { node: ">=0.10.0" } wrap-ansi@9.0.2: - resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== + } + engines: { node: ">=18" } yaml@2.8.3: - resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} - engines: {node: '>= 14.6'} + resolution: + { + integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg== + } + engines: { node: ">= 14.6" } hasBin: true yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + } + engines: { node: ">=10" } snapshots: - - '@eslint-community/eslint-utils@4.9.1(eslint@10.2.0)': + "@eslint-community/eslint-utils@4.9.1(eslint@10.2.0)": dependencies: eslint: 10.2.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.2': {} + "@eslint-community/regexpp@4.12.2": {} - '@eslint/config-array@0.23.4': + "@eslint/config-array@0.23.4": dependencies: - '@eslint/object-schema': 3.0.4 + "@eslint/object-schema": 3.0.4 debug: 4.4.3 minimatch: 10.2.5 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.5.4': + "@eslint/config-helpers@0.5.4": dependencies: - '@eslint/core': 1.2.0 + "@eslint/core": 1.2.0 - '@eslint/core@1.2.0': + "@eslint/core@1.2.0": dependencies: - '@types/json-schema': 7.0.15 + "@types/json-schema": 7.0.15 - '@eslint/js@10.0.1(eslint@10.2.0)': + "@eslint/js@10.0.1(eslint@10.2.0)": optionalDependencies: eslint: 10.2.0 - '@eslint/object-schema@3.0.4': {} + "@eslint/object-schema@3.0.4": {} - '@eslint/plugin-kit@0.7.0': + "@eslint/plugin-kit@0.7.0": dependencies: - '@eslint/core': 1.2.0 + "@eslint/core": 1.2.0 levn: 0.4.1 - '@humanfs/core@0.19.1': {} + "@humanfs/core@0.19.1": {} - '@humanfs/node@0.16.7': + "@humanfs/node@0.16.7": dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.4.3 + "@humanfs/core": 0.19.1 + "@humanwhocodes/retry": 0.4.3 - '@humanwhocodes/module-importer@1.0.1': {} + "@humanwhocodes/module-importer@1.0.1": {} - '@humanwhocodes/retry@0.4.3': {} + "@humanwhocodes/retry@0.4.3": {} - '@turbo/darwin-64@2.9.3': + "@turbo/darwin-64@2.9.3": optional: true - '@turbo/darwin-arm64@2.9.3': + "@turbo/darwin-arm64@2.9.3": optional: true - '@turbo/linux-64@2.9.3': + "@turbo/linux-64@2.9.3": optional: true - '@turbo/linux-arm64@2.9.3': + "@turbo/linux-arm64@2.9.3": optional: true - '@turbo/windows-64@2.9.3': + "@turbo/windows-64@2.9.3": optional: true - '@turbo/windows-arm64@2.9.3': + "@turbo/windows-arm64@2.9.3": optional: true - '@types/esrecurse@4.3.1': {} + "@types/esrecurse@4.3.1": {} - '@types/estree@1.0.8': {} + "@types/estree@1.0.8": {} - '@types/json-schema@7.0.15': {} + "@types/json-schema@7.0.15": {} acorn-jsx@5.3.2(acorn@8.16.0): dependencies: @@ -605,8 +937,8 @@ snapshots: eslint-scope@9.1.2: dependencies: - '@types/esrecurse': 4.3.1 - '@types/estree': 1.0.8 + "@types/esrecurse": 4.3.1 + "@types/estree": 1.0.8 esrecurse: 4.3.0 estraverse: 5.3.0 @@ -616,16 +948,16 @@ snapshots: eslint@10.2.0: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.0) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.23.4 - '@eslint/config-helpers': 0.5.4 - '@eslint/core': 1.2.0 - '@eslint/plugin-kit': 0.7.0 - '@humanfs/node': 0.16.7 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 + "@eslint-community/eslint-utils": 4.9.1(eslint@10.2.0) + "@eslint-community/regexpp": 4.12.2 + "@eslint/config-array": 0.23.4 + "@eslint/config-helpers": 0.5.4 + "@eslint/core": 1.2.0 + "@eslint/plugin-kit": 0.7.0 + "@humanfs/node": 0.16.7 + "@humanwhocodes/module-importer": 1.0.1 + "@humanwhocodes/retry": 0.4.3 + "@types/estree": 1.0.8 ajv: 6.14.0 cross-spawn: 7.0.6 debug: 4.4.3 @@ -699,6 +1031,8 @@ snapshots: globals@17.4.0: {} + husky@9.1.7: {} + ignore@5.3.2: {} imurmurhash@0.1.4: {} @@ -849,12 +1183,12 @@ snapshots: turbo@2.9.3: optionalDependencies: - '@turbo/darwin-64': 2.9.3 - '@turbo/darwin-arm64': 2.9.3 - '@turbo/linux-64': 2.9.3 - '@turbo/linux-arm64': 2.9.3 - '@turbo/windows-64': 2.9.3 - '@turbo/windows-arm64': 2.9.3 + "@turbo/darwin-64": 2.9.3 + "@turbo/darwin-arm64": 2.9.3 + "@turbo/linux-64": 2.9.3 + "@turbo/linux-arm64": 2.9.3 + "@turbo/windows-64": 2.9.3 + "@turbo/windows-arm64": 2.9.3 type-check@0.4.0: dependencies: From 80ff8ab7651667d9267d986be1800d6a9430fd5b Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 12:26:32 +0800 Subject: [PATCH 03/22] docs(contributing): define commit and branch conventions --- CONTRIBUTING.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..728d44f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,69 @@ +# 贡献指南(Contributing) + +本文档定义 TodoList 仓库的协作规范,所有贡献者提交代码前请先阅读。 + +## 1. 分支模型 + +- 长期分支: + - `main`:生产稳定分支 + - `develop`:开发集成分支 +- 功能分支: + - 命名:`feature/-` + - 示例:`feature/p1-code-quality-hooks` +- 其他分支: + - `release/` + - `hotfix/-` + +## 2. 提交流程 + +1. 从目标基线分支切出功能分支。 +2. 每完成一个小功能,提交一个最小 commit。 +3. 完成后推送分支并创建 PR。 +4. 通过 Code Review 后再合并到目标分支。 + +## 3. Commit 规范 + +- 使用 Conventional Commits: + - `feat(scope): ...` + - `fix(scope): ...` + - `chore(scope): ...` + - `docs(scope): ...` + - `test(scope): ...` + - `ci(scope): ...` +- 要求: + - commit 粒度最小化,不要把多个不相关改动塞进一个提交。 + - commit 必须可回滚、可解释。 + - 默认使用 GPG 签名提交:`git commit -S`。 + +## 4. PR 规范 + +- PR 标题简明描述变更目标。 +- PR 描述至少包含: + - 变更概述 + - 具体改动 + - 测试结果 + - 风险评估 + - 回滚方案 +- 一个 PR 只解决一类问题,避免“超大 PR”。 + +## 5. 代码质量检查 + +提交前建议至少执行: + +```bash +pnpm install +pnpm run lint +pnpm run typecheck +pnpm run test +``` + +说明: + +- `pre-commit` 会自动执行 `lint-staged`。 +- `pre-push` 会自动执行 `typecheck + test`。 + +## 6. 变更边界要求 + +- 不要提交无关文件(例如本地 IDE 缓存、临时导出文件)。 +- 不要随意修改与当前任务无关的历史代码。 +- 如发现仓库出现非本人预期改动,先暂停并和维护者确认。 From 7fbfac6b87aaf3ce5244fdfedeea58042e16fc81 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 12:46:01 +0800 Subject: [PATCH 04/22] ci(actions): add pull request quality workflow --- .github/workflows/pr-quality.yml | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/pr-quality.yml diff --git a/.github/workflows/pr-quality.yml b/.github/workflows/pr-quality.yml new file mode 100644 index 0000000..2dbdb43 --- /dev/null +++ b/.github/workflows/pr-quality.yml @@ -0,0 +1,46 @@ +name: PR Quality + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + branches: [main, develop] + +concurrency: + group: pr-quality-${{ github.ref }} + cancel-in-progress: true + +jobs: + quality: + name: Lint, Typecheck, Test, Build + runs-on: ubuntu-latest + if: github.event.pull_request.draft == false + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9.15.2 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Lint + run: pnpm run lint + + - name: Typecheck + run: pnpm run typecheck + + - name: Test + run: pnpm run test + + - name: Build + run: pnpm run build From 44cc2a0ee6e536e3c380dbf151f85415a449c736 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 12:46:55 +0800 Subject: [PATCH 05/22] ci(actions): add api docker image workflow --- .github/workflows/api-docker-image.yml | 66 ++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/api-docker-image.yml diff --git a/.github/workflows/api-docker-image.yml b/.github/workflows/api-docker-image.yml new file mode 100644 index 0000000..685084a --- /dev/null +++ b/.github/workflows/api-docker-image.yml @@ -0,0 +1,66 @@ +name: API Docker Image + +on: + pull_request: + branches: [main, develop] + paths: + - "apps/api/**" + - ".github/workflows/api-docker-image.yml" + push: + branches: [main] + paths: + - "apps/api/**" + - ".github/workflows/api-docker-image.yml" + workflow_dispatch: + +concurrency: + group: api-docker-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-publish: + name: Build API Docker Image + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check Dockerfile + id: dockerfile + run: | + if [ -f apps/api/Dockerfile ]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + fi + + - name: Setup Docker Buildx + if: steps.dockerfile.outputs.exists == 'true' + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + if: steps.dockerfile.outputs.exists == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build (PR/manual) or Build and Push (main) + if: steps.dockerfile.outputs.exists == 'true' + uses: docker/build-push-action@v6 + with: + context: ./apps/api + file: ./apps/api/Dockerfile + push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + tags: | + ghcr.io/${{ github.repository }}/api:${{ github.sha }} + ghcr.io/${{ github.repository }}/api:latest + + - name: Skip notice + if: steps.dockerfile.outputs.exists != 'true' + run: echo "apps/api/Dockerfile not found, skip docker build." From d5677107683c479c9e5dc02543d0033f1df9f911 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 12:47:58 +0800 Subject: [PATCH 06/22] ci(actions): add web and admin deploy workflow templates --- .github/workflows/deploy-admin.yml | 59 ++++++++++++++++++++++++++++++ .github/workflows/deploy-web.yml | 59 ++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 .github/workflows/deploy-admin.yml create mode 100644 .github/workflows/deploy-web.yml diff --git a/.github/workflows/deploy-admin.yml b/.github/workflows/deploy-admin.yml new file mode 100644 index 0000000..4c1f691 --- /dev/null +++ b/.github/workflows/deploy-admin.yml @@ -0,0 +1,59 @@ +name: Deploy Admin + +on: + push: + branches: [main] + paths: + - "apps/admin/**" + - "packages/shared-types/**" + - "packages/ui/**" + - ".github/workflows/deploy-admin.yml" + workflow_dispatch: + +concurrency: + group: deploy-admin-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build Admin + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9.15.2 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build workspace + run: pnpm run build + + deploy: + name: Deploy Admin (Template) + runs-on: ubuntu-latest + needs: build + + steps: + - name: Trigger deployment webhook + env: + ADMIN_DEPLOY_WEBHOOK_URL: ${{ secrets.ADMIN_DEPLOY_WEBHOOK_URL }} + run: | + if [ -z "$ADMIN_DEPLOY_WEBHOOK_URL" ]; then + echo "ADMIN_DEPLOY_WEBHOOK_URL is not configured. Skipping deploy." + exit 0 + fi + + curl -X POST "$ADMIN_DEPLOY_WEBHOOK_URL" + echo "Admin deployment webhook triggered." diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml new file mode 100644 index 0000000..a55a8be --- /dev/null +++ b/.github/workflows/deploy-web.yml @@ -0,0 +1,59 @@ +name: Deploy Web + +on: + push: + branches: [main] + paths: + - "apps/web/**" + - "packages/shared-types/**" + - "packages/ui/**" + - ".github/workflows/deploy-web.yml" + workflow_dispatch: + +concurrency: + group: deploy-web-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build Web + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9.15.2 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build workspace + run: pnpm run build + + deploy: + name: Deploy Web (Template) + runs-on: ubuntu-latest + needs: build + + steps: + - name: Trigger deployment webhook + env: + WEB_DEPLOY_WEBHOOK_URL: ${{ secrets.WEB_DEPLOY_WEBHOOK_URL }} + run: | + if [ -z "$WEB_DEPLOY_WEBHOOK_URL" ]; then + echo "WEB_DEPLOY_WEBHOOK_URL is not configured. Skipping deploy." + exit 0 + fi + + curl -X POST "$WEB_DEPLOY_WEBHOOK_URL" + echo "Web deployment webhook triggered." From fba9443a30534528abdfc4d3b7f2c3e6a34b4313 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 12:58:14 +0800 Subject: [PATCH 07/22] feat(api-db): initialize prisma with postgresql datasource --- apps/api/.env.example | 1 + apps/api/.gitignore | 5 + apps/api/.gitkeep | 0 apps/api/package.json | 19 + apps/api/prisma.config.ts | 14 + apps/api/prisma/schema.prisma | 13 + pnpm-lock.yaml | 1130 ++++++++++++++++++++++++++++++++- 7 files changed, 1174 insertions(+), 8 deletions(-) create mode 100644 apps/api/.env.example create mode 100644 apps/api/.gitignore delete mode 100644 apps/api/.gitkeep create mode 100644 apps/api/package.json create mode 100644 apps/api/prisma.config.ts create mode 100644 apps/api/prisma/schema.prisma diff --git a/apps/api/.env.example b/apps/api/.env.example new file mode 100644 index 0000000..203f3a0 --- /dev/null +++ b/apps/api/.env.example @@ -0,0 +1 @@ +DATABASE_URL="postgresql://postgres:postgres@localhost:5432/todolist?schema=public" diff --git a/apps/api/.gitignore b/apps/api/.gitignore new file mode 100644 index 0000000..9f62ec0 --- /dev/null +++ b/apps/api/.gitignore @@ -0,0 +1,5 @@ +node_modules +# Keep environment variables out of version control +.env + +/generated/prisma diff --git a/apps/api/.gitkeep b/apps/api/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 0000000..fd3daf9 --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,19 @@ +{ + "name": "@todolist/api", + "version": "0.1.0", + "description": "TodoList API service", + "scripts": { + "prisma:generate": "prisma generate", + "prisma:format": "prisma format", + "prisma:validate": "prisma validate" + }, + "license": "GPL-3.0-or-later", + "devDependencies": { + "dotenv": "^16.6.1", + "prisma": "^7.6.0" + }, + "private": true, + "dependencies": { + "@prisma/client": "^7.6.0" + } +} diff --git a/apps/api/prisma.config.ts b/apps/api/prisma.config.ts new file mode 100644 index 0000000..8f3a0eb --- /dev/null +++ b/apps/api/prisma.config.ts @@ -0,0 +1,14 @@ +// This file was generated by Prisma, and assumes you have installed the following: +// npm install --save-dev prisma dotenv +import "dotenv/config"; +import { defineConfig } from "prisma/config"; + +export default defineConfig({ + schema: "prisma/schema.prisma", + migrations: { + path: "prisma/migrations" + }, + datasource: { + url: process.env["DATABASE_URL"] + } +}); diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma new file mode 100644 index 0000000..8bddec1 --- /dev/null +++ b/apps/api/prisma/schema.prisma @@ -0,0 +1,13 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Get a free hosted Postgres database in seconds: `npx create-db` + +generator client { + provider = "prisma-client" + output = "../generated/prisma" +} + +datasource db { + provider = "postgresql" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3c014fa..48e6d7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,10 +9,10 @@ importers: devDependencies: "@eslint/js": specifier: ^10.0.1 - version: 10.0.1(eslint@10.2.0) + version: 10.0.1(eslint@10.2.0(jiti@2.6.1)) eslint: specifier: ^10.2.0 - version: 10.2.0 + version: 10.2.0(jiti@2.6.1) globals: specifier: ^17.4.0 version: 17.4.0 @@ -29,11 +29,59 @@ importers: specifier: ^2.9.3 version: 2.9.3 + apps/api: + dependencies: + "@prisma/client": + specifier: ^7.6.0 + version: 7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + devDependencies: + dotenv: + specifier: ^16.6.1 + version: 16.6.1 + prisma: + specifier: ^7.6.0 + version: 7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + packages/eslint-config: {} packages/tsconfig: {} packages: + "@clack/core@0.5.0": + resolution: + { + integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow== + } + + "@clack/prompts@0.11.0": + resolution: + { + integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw== + } + + "@electric-sql/pglite-socket@0.1.1": + resolution: + { + integrity: sha512-p2hoXw3Z3LQHwTeikdZNsFBOvXGqKY2hk51BBw+8NKND8eoH+8LFOtW9Z8CQKmTJ2qqGYu82ipqiyFZOTTXNfw== + } + hasBin: true + peerDependencies: + "@electric-sql/pglite": 0.4.1 + + "@electric-sql/pglite-tools@0.3.1": + resolution: + { + integrity: sha512-C+T3oivmy9bpQvSxVqXA1UDY8cB9Eb9vZHL9zxWwEUfDixbXv4G3r2LjoTdR33LD8aomR3O9ZXEO3XEwr/cUCA== + } + peerDependencies: + "@electric-sql/pglite": 0.4.1 + + "@electric-sql/pglite@0.4.1": + resolution: + { + integrity: sha512-mZ9NzzUSYPOCnxHH1oAHPRzoMFJHY472raDKwXl/+6oPbpdJ7g8LsCN4FSaIIfkiCKHhb3iF/Zqo3NYxaIhU7Q== + } + "@eslint-community/eslint-utils@4.9.1": resolution: { @@ -97,6 +145,15 @@ packages: } engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + "@hono/node-server@1.19.11": + resolution: + { + integrity: sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g== + } + engines: { node: ">=18.14.1" } + peerDependencies: + hono: ^4 + "@humanfs/core@0.19.1": resolution: { @@ -125,6 +182,215 @@ packages: } engines: { node: ">=18.18" } + "@kurkle/color@0.3.4": + resolution: + { + integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w== + } + + "@prisma/client-runtime-utils@7.6.0": + resolution: + { + integrity: sha512-fD7jlqubsZvVODKvsp9lOpXVecx2aWGxC2l35Ioz2t+teUJ5CfR0SAMsi7UkU1VvaZmmm+DS6BdujF622nY7tQ== + } + + "@prisma/client@7.6.0": + resolution: + { + integrity: sha512-7Pe/1ayh3GgWPEg4mmT4ax77LJ1wC+XlnIFvQ94bLP2DsUnOpnruQQR3Jw7r+Frthk94QqDNxo3FjSg8h9PXeQ== + } + engines: { node: ^20.19 || ^22.12 || >=24.0 } + peerDependencies: + prisma: "*" + typescript: ">=5.4.0" + peerDependenciesMeta: + prisma: + optional: true + typescript: + optional: true + + "@prisma/config@7.6.0": + resolution: + { + integrity: sha512-MuAz1MK4PeG5/03YzfzX3CnFVHQ6qePGwUpQRzPzX5tT0ffJ3Tzi9zJZbBc+VzEGFCM8ghW/gTVDR85Syjt+Yw== + } + + "@prisma/debug@7.2.0": + resolution: + { + integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw== + } + + "@prisma/debug@7.6.0": + resolution: + { + integrity: sha512-LpHr3qos4lQZ6sxwjStf59YBht7m9/QF7NSQsMH6qGENWZu2w3UkQUGn1h5iRkDjnWRj3VHykOu9qFhps4ADvA== + } + + "@prisma/dev@0.24.3": + resolution: + { + integrity: sha512-ffHlQuKXZiaDt9Go0OnCTdJZrHxK0k7omJKNV86/VjpsXu5EIHZLK0T7JSWgvNlJwh56kW9JFu9v0qJciFzepg== + } + + "@prisma/engines-version@7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711": + resolution: + { + integrity: sha512-r51DLcJ8bDRSrBEJF3J4cinoWyGA7rfP2mG6lD90VqIbGNOkbfcLcXalSVjq5Y6brQS3vcjrq4GbyUb1Cb7vkw== + } + + "@prisma/engines@7.6.0": + resolution: + { + integrity: sha512-Sn5edRzhHqgRV2M+A0eIbY442B4mReWWf3pKs/LKreYgW7oa/up8JtK/s4iv/EQA097cyboZ08mmkpbLp+tZ3w== + } + + "@prisma/fetch-engine@7.6.0": + resolution: + { + integrity: sha512-N575Ni95c3FkduWY/eKTHqNYgNbceZ1tQaSknVtJjpKmiiBXmniESn/GTxsDvICC4ZeiNrXxioGInzQrCdx16w== + } + + "@prisma/get-platform@7.2.0": + resolution: + { + integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA== + } + + "@prisma/get-platform@7.6.0": + resolution: + { + integrity: sha512-ohZDwXvtmnbzOcutR2D13lDWpZP1wQjmPyztmt0AwXLzQI7q95EE7NYCvS+M6N6SivT+BM0NOqLmTH3wms4L3A== + } + + "@prisma/query-plan-executor@7.2.0": + resolution: + { + integrity: sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ== + } + + "@prisma/streams-local@0.1.2": + resolution: + { + integrity: sha512-l49yTxKKF2odFxaAXTmwmkBKL3+bVQ1tFOooGifu4xkdb9NMNLxHj27XAhTylWZod8I+ISGM5erU1xcl/oBCtg== + } + engines: { bun: ">=1.3.6", node: ">=22.0.0" } + + "@prisma/studio-core@0.27.3": + resolution: + { + integrity: sha512-AADjNFPdsrglxHQVTmHFqv6DuKQZ5WY4p5/gVFY017twvNrSwpLJ9lqUbYYxEu2W7nbvVxTZA8deJ8LseNALsw== + } + engines: { node: ^20.19 || ^22.12 || >=24.0, pnpm: "8" } + peerDependencies: + "@types/react": ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + "@radix-ui/primitive@1.1.3": + resolution: + { + integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg== + } + + "@radix-ui/react-compose-refs@1.1.2": + resolution: + { + integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg== + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + + "@radix-ui/react-primitive@2.1.3": + resolution: + { + integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ== + } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-slot@1.2.3": + resolution: + { + integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A== + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + + "@radix-ui/react-toggle@1.1.10": + resolution: + { + integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ== + } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + + "@radix-ui/react-use-controllable-state@1.2.2": + resolution: + { + integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg== + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + + "@radix-ui/react-use-effect-event@0.0.2": + resolution: + { + integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA== + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + + "@radix-ui/react-use-layout-effect@1.1.1": + resolution: + { + integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ== + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + + "@standard-schema/spec@1.1.0": + resolution: + { + integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== + } + "@turbo/darwin-64@2.9.3": resolution: { @@ -191,6 +457,12 @@ packages: integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== } + "@types/react@19.2.14": + resolution: + { + integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w== + } + acorn-jsx@5.3.2: resolution: { @@ -213,6 +485,12 @@ packages: integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw== } + ajv@8.18.0: + resolution: + { + integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A== + } + ansi-escapes@7.3.0: resolution: { @@ -234,6 +512,13 @@ packages: } engines: { node: ">=12" } + aws-ssl-profiles@1.1.2: + resolution: + { + integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g== + } + engines: { node: ">= 6.0.0" } + balanced-match@4.0.4: resolution: { @@ -241,6 +526,13 @@ packages: } engines: { node: 18 || 20 || >=22 } + better-result@2.7.0: + resolution: + { + integrity: sha512-7zrmXjAK8u8Z6SOe4R65XObOR5X+Y2I/VVku3t5cPOGQ8/WsBcfFmfnIPiEl5EBMDOzPHRwbiPbMtQBKYdw7RA== + } + hasBin: true + brace-expansion@5.0.5: resolution: { @@ -248,6 +540,43 @@ packages: } engines: { node: 18 || 20 || >=22 } + c12@3.1.0: + resolution: + { + integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw== + } + peerDependencies: + magicast: ^0.3.5 + peerDependenciesMeta: + magicast: + optional: true + + chart.js@4.5.1: + resolution: + { + integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw== + } + engines: { pnpm: ">=8" } + + chokidar@4.0.3: + resolution: + { + integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + } + engines: { node: ">= 14.16.0" } + + citty@0.1.6: + resolution: + { + integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ== + } + + citty@0.2.2: + resolution: + { + integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w== + } + cli-cursor@5.0.0: resolution: { @@ -275,6 +604,19 @@ packages: } engines: { node: ">=20" } + confbox@0.2.4: + resolution: + { + integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ== + } + + consola@3.4.2: + resolution: + { + integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA== + } + engines: { node: ^14.18.0 || >=16.10.0 } + cross-spawn@7.0.6: resolution: { @@ -282,6 +624,12 @@ packages: } engines: { node: ">= 8" } + csstype@3.2.3: + resolution: + { + integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + } + debug@4.4.3: resolution: { @@ -300,12 +648,65 @@ packages: integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== } + deepmerge-ts@7.1.5: + resolution: + { + integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw== + } + engines: { node: ">=16.0.0" } + + defu@6.1.6: + resolution: + { + integrity: sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug== + } + + denque@2.1.0: + resolution: + { + integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + } + engines: { node: ">=0.10" } + + destr@2.0.5: + resolution: + { + integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA== + } + + dotenv@16.6.1: + resolution: + { + integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow== + } + engines: { node: ">=12" } + + effect@3.20.0: + resolution: + { + integrity: sha512-qMLfDJscrNG8p/aw+IkT9W7fgj50Z4wG5bLBy0Txsxz8iUHjDIkOgO3SV0WZfnQbNG2VJYb0b+rDLMrhM4+Krw== + } + emoji-regex@10.6.0: resolution: { integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== } + empathic@2.0.0: + resolution: + { + integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA== + } + engines: { node: ">=14" } + + env-paths@3.0.0: + resolution: + { + integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A== + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + environment@1.1.0: resolution: { @@ -395,6 +796,19 @@ packages: integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== } + exsolve@1.0.8: + resolution: + { + integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA== + } + + fast-check@3.23.2: + resolution: + { + integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A== + } + engines: { node: ">=8.0.0" } + fast-deep-equal@3.1.3: resolution: { @@ -413,6 +827,12 @@ packages: integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== } + fast-uri@3.1.0: + resolution: + { + integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + } + file-entry-cache@8.0.0: resolution: { @@ -440,6 +860,19 @@ packages: integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== } + foreground-child@3.3.1: + resolution: + { + integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + } + engines: { node: ">=14" } + + generate-function@2.3.1: + resolution: + { + integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + } + get-east-asian-width@1.5.0: resolution: { @@ -447,6 +880,19 @@ packages: } engines: { node: ">=18" } + get-port-please@3.2.0: + resolution: + { + integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A== + } + + giget@2.0.0: + resolution: + { + integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA== + } + hasBin: true + glob-parent@6.0.2: resolution: { @@ -461,6 +907,37 @@ packages: } engines: { node: ">=18" } + graceful-fs@4.2.11: + resolution: + { + integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + } + + grammex@3.1.12: + resolution: + { + integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ== + } + + graphmatch@1.1.1: + resolution: + { + integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg== + } + + hono@4.12.10: + resolution: + { + integrity: sha512-mx/p18PLy5og9ufies2GOSUqep98Td9q4i/EF6X7yJgAiIopxqdfIO3jbqsi3jRgTgw88jMDEzVKi+V2EF+27w== + } + engines: { node: ">=16.9.0" } + + http-status-codes@2.3.0: + resolution: + { + integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA== + } + husky@9.1.7: resolution: { @@ -469,6 +946,13 @@ packages: engines: { node: ">=18" } hasBin: true + iconv-lite@0.7.2: + resolution: + { + integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== + } + engines: { node: ">=0.10.0" } + ignore@5.3.2: resolution: { @@ -504,12 +988,25 @@ packages: } engines: { node: ">=0.10.0" } + is-property@1.0.2: + resolution: + { + integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== + } + isexe@2.0.0: resolution: { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== } + jiti@2.6.1: + resolution: + { + integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== + } + hasBin: true + json-buffer@3.0.1: resolution: { @@ -522,6 +1019,12 @@ packages: integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== } + json-schema-traverse@1.0.0: + resolution: + { + integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + } + json-stable-stringify-without-jsonify@1.0.1: resolution: { @@ -570,6 +1073,19 @@ packages: } engines: { node: ">=18" } + long@5.3.2: + resolution: + { + integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== + } + + lru.min@1.1.4: + resolution: + { + integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA== + } + engines: { bun: ">=1.0.0", deno: ">=1.30.0", node: ">=8.0.0" } + mimic-function@5.0.1: resolution: { @@ -590,12 +1106,46 @@ packages: integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== } + mysql2@3.15.3: + resolution: + { + integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg== + } + engines: { node: ">= 8.0" } + + named-placeholders@1.1.6: + resolution: + { + integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w== + } + engines: { node: ">=8.0.0" } + natural-compare@1.4.0: resolution: { integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== } + node-fetch-native@1.6.7: + resolution: + { + integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q== + } + + nypm@0.6.5: + resolution: + { + integrity: sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ== + } + engines: { node: ">=18" } + hasBin: true + + ohash@2.0.11: + resolution: + { + integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ== + } + onetime@7.0.0: resolution: { @@ -638,6 +1188,24 @@ packages: } engines: { node: ">=8" } + pathe@2.0.3: + resolution: + { + integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + } + + perfect-debounce@1.0.0: + resolution: + { + integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== + } + + picocolors@1.1.1: + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + } + picomatch@4.0.4: resolution: { @@ -645,6 +1213,19 @@ packages: } engines: { node: ">=12" } + pkg-types@2.3.0: + resolution: + { + integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig== + } + + postgres@3.4.7: + resolution: + { + integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw== + } + engines: { node: ">=12" } + prelude-ls@1.2.1: resolution: { @@ -660,6 +1241,28 @@ packages: engines: { node: ">=14" } hasBin: true + prisma@7.6.0: + resolution: + { + integrity: sha512-OKJIPT81K3+F+AayIkY/Y3mkF2NWoFh7lZApaaqPYy7EHILKdO0VsmGkP+hDKYTySHsFSyLWXm/JgcR1B8fY1Q== + } + engines: { node: ^20.19 || ^22.12 || >=24.0 } + hasBin: true + peerDependencies: + better-sqlite3: ">=9.0.0" + typescript: ">=5.4.0" + peerDependenciesMeta: + better-sqlite3: + optional: true + typescript: + optional: true + + proper-lockfile@4.1.2: + resolution: + { + integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + } + punycode@2.3.1: resolution: { @@ -667,6 +1270,53 @@ packages: } engines: { node: ">=6" } + pure-rand@6.1.0: + resolution: + { + integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + } + + rc9@2.1.2: + resolution: + { + integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg== + } + + react-dom@19.2.4: + resolution: + { + integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ== + } + peerDependencies: + react: ^19.2.4 + + react@19.2.4: + resolution: + { + integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ== + } + engines: { node: ">=0.10.0" } + + readdirp@4.1.2: + resolution: + { + integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + } + engines: { node: ">= 14.18.0" } + + remeda@2.33.4: + resolution: + { + integrity: sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ== + } + + require-from-string@2.0.2: + resolution: + { + integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + } + engines: { node: ">=0.10.0" } + restore-cursor@5.1.0: resolution: { @@ -674,12 +1324,37 @@ packages: } engines: { node: ">=18" } + retry@0.12.0: + resolution: + { + integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + } + engines: { node: ">= 4" } + rfdc@1.4.1: resolution: { integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== } + safer-buffer@2.1.2: + resolution: + { + integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + } + + scheduler@0.27.0: + resolution: + { + integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== + } + + seq-queue@0.0.5: + resolution: + { + integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q== + } + shebang-command@2.0.0: resolution: { @@ -694,6 +1369,12 @@ packages: } engines: { node: ">=8" } + signal-exit@3.0.7: + resolution: + { + integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + } + signal-exit@4.1.0: resolution: { @@ -701,6 +1382,12 @@ packages: } engines: { node: ">=14" } + sisteransi@1.0.5: + resolution: + { + integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + } + slice-ansi@7.1.2: resolution: { @@ -715,6 +1402,19 @@ packages: } engines: { node: ">=20" } + sqlstring@2.3.3: + resolution: + { + integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg== + } + engines: { node: ">= 0.6" } + + std-env@3.10.0: + resolution: + { + integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg== + } + string-argv@0.3.2: resolution: { @@ -770,6 +1470,17 @@ packages: integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== } + valibot@1.2.0: + resolution: + { + integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg== + } + peerDependencies: + typescript: ">=5" + peerDependenciesMeta: + typescript: + optional: true + which@2.0.2: resolution: { @@ -807,10 +1518,37 @@ packages: } engines: { node: ">=10" } + zeptomatch@2.1.0: + resolution: + { + integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA== + } + snapshots: - "@eslint-community/eslint-utils@4.9.1(eslint@10.2.0)": + "@clack/core@0.5.0": dependencies: - eslint: 10.2.0 + picocolors: 1.1.1 + sisteransi: 1.0.5 + + "@clack/prompts@0.11.0": + dependencies: + "@clack/core": 0.5.0 + picocolors: 1.1.1 + sisteransi: 1.0.5 + + "@electric-sql/pglite-socket@0.1.1(@electric-sql/pglite@0.4.1)": + dependencies: + "@electric-sql/pglite": 0.4.1 + + "@electric-sql/pglite-tools@0.3.1(@electric-sql/pglite@0.4.1)": + dependencies: + "@electric-sql/pglite": 0.4.1 + + "@electric-sql/pglite@0.4.1": {} + + "@eslint-community/eslint-utils@4.9.1(eslint@10.2.0(jiti@2.6.1))": + dependencies: + eslint: 10.2.0(jiti@2.6.1) eslint-visitor-keys: 3.4.3 "@eslint-community/regexpp@4.12.2": {} @@ -831,9 +1569,9 @@ snapshots: dependencies: "@types/json-schema": 7.0.15 - "@eslint/js@10.0.1(eslint@10.2.0)": + "@eslint/js@10.0.1(eslint@10.2.0(jiti@2.6.1))": optionalDependencies: - eslint: 10.2.0 + eslint: 10.2.0(jiti@2.6.1) "@eslint/object-schema@3.0.4": {} @@ -842,6 +1580,10 @@ snapshots: "@eslint/core": 1.2.0 levn: 0.4.1 + "@hono/node-server@1.19.11(hono@4.12.10)": + dependencies: + hono: 4.12.10 + "@humanfs/core@0.19.1": {} "@humanfs/node@0.16.7": @@ -853,6 +1595,149 @@ snapshots: "@humanwhocodes/retry@0.4.3": {} + "@kurkle/color@0.3.4": {} + + "@prisma/client-runtime-utils@7.6.0": {} + + "@prisma/client@7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))": + dependencies: + "@prisma/client-runtime-utils": 7.6.0 + optionalDependencies: + prisma: 7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + "@prisma/config@7.6.0": + dependencies: + c12: 3.1.0 + deepmerge-ts: 7.1.5 + effect: 3.20.0 + empathic: 2.0.0 + transitivePeerDependencies: + - magicast + + "@prisma/debug@7.2.0": {} + + "@prisma/debug@7.6.0": {} + + "@prisma/dev@0.24.3": + dependencies: + "@electric-sql/pglite": 0.4.1 + "@electric-sql/pglite-socket": 0.1.1(@electric-sql/pglite@0.4.1) + "@electric-sql/pglite-tools": 0.3.1(@electric-sql/pglite@0.4.1) + "@hono/node-server": 1.19.11(hono@4.12.10) + "@prisma/get-platform": 7.2.0 + "@prisma/query-plan-executor": 7.2.0 + "@prisma/streams-local": 0.1.2 + foreground-child: 3.3.1 + get-port-please: 3.2.0 + hono: 4.12.10 + http-status-codes: 2.3.0 + pathe: 2.0.3 + proper-lockfile: 4.1.2 + remeda: 2.33.4 + std-env: 3.10.0 + valibot: 1.2.0 + zeptomatch: 2.1.0 + transitivePeerDependencies: + - typescript + + "@prisma/engines-version@7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711": {} + + "@prisma/engines@7.6.0": + dependencies: + "@prisma/debug": 7.6.0 + "@prisma/engines-version": 7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711 + "@prisma/fetch-engine": 7.6.0 + "@prisma/get-platform": 7.6.0 + + "@prisma/fetch-engine@7.6.0": + dependencies: + "@prisma/debug": 7.6.0 + "@prisma/engines-version": 7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711 + "@prisma/get-platform": 7.6.0 + + "@prisma/get-platform@7.2.0": + dependencies: + "@prisma/debug": 7.2.0 + + "@prisma/get-platform@7.6.0": + dependencies: + "@prisma/debug": 7.6.0 + + "@prisma/query-plan-executor@7.2.0": {} + + "@prisma/streams-local@0.1.2": + dependencies: + ajv: 8.18.0 + better-result: 2.7.0 + env-paths: 3.0.0 + proper-lockfile: 4.1.2 + + "@prisma/studio-core@0.27.3(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)": + dependencies: + "@radix-ui/react-toggle": 1.1.10(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + "@types/react": 19.2.14 + chart.js: 4.5.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - "@types/react-dom" + + "@radix-ui/primitive@1.1.3": {} + + "@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.4)": + dependencies: + react: 19.2.4 + optionalDependencies: + "@types/react": 19.2.14 + + "@radix-ui/react-primitive@2.1.3(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)": + dependencies: + "@radix-ui/react-slot": 1.2.3(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + "@types/react": 19.2.14 + + "@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.2.4)": + dependencies: + "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + "@types/react": 19.2.14 + + "@radix-ui/react-toggle@1.1.10(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)": + dependencies: + "@radix-ui/primitive": 1.1.3 + "@radix-ui/react-primitive": 2.1.3(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + "@types/react": 19.2.14 + + "@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.14)(react@19.2.4)": + dependencies: + "@radix-ui/react-use-effect-event": 0.0.2(@types/react@19.2.14)(react@19.2.4) + "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + "@types/react": 19.2.14 + + "@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.14)(react@19.2.4)": + dependencies: + "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + "@types/react": 19.2.14 + + "@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.2.4)": + dependencies: + react: 19.2.4 + optionalDependencies: + "@types/react": 19.2.14 + + "@standard-schema/spec@1.1.0": {} + "@turbo/darwin-64@2.9.3": optional: true @@ -877,6 +1762,10 @@ snapshots: "@types/json-schema@7.0.15": {} + "@types/react@19.2.14": + dependencies: + csstype: 3.2.3 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -890,6 +1779,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-escapes@7.3.0: dependencies: environment: 1.1.0 @@ -898,12 +1794,47 @@ snapshots: ansi-styles@6.2.3: {} + aws-ssl-profiles@1.1.2: {} + balanced-match@4.0.4: {} + better-result@2.7.0: + dependencies: + "@clack/prompts": 0.11.0 + brace-expansion@5.0.5: dependencies: balanced-match: 4.0.4 + c12@3.1.0: + dependencies: + chokidar: 4.0.3 + confbox: 0.2.4 + defu: 6.1.6 + dotenv: 16.6.1 + exsolve: 1.0.8 + giget: 2.0.0 + jiti: 2.6.1 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 1.0.0 + pkg-types: 2.3.0 + rc9: 2.1.2 + + chart.js@4.5.1: + dependencies: + "@kurkle/color": 0.3.4 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + citty@0.1.6: + dependencies: + consola: 3.4.2 + + citty@0.2.2: {} + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -917,20 +1848,45 @@ snapshots: commander@14.0.3: {} + confbox@0.2.4: {} + + consola@3.4.2: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + csstype@3.2.3: {} + debug@4.4.3: dependencies: ms: 2.1.3 deep-is@0.1.4: {} + deepmerge-ts@7.1.5: {} + + defu@6.1.6: {} + + denque@2.1.0: {} + + destr@2.0.5: {} + + dotenv@16.6.1: {} + + effect@3.20.0: + dependencies: + "@standard-schema/spec": 1.1.0 + fast-check: 3.23.2 + emoji-regex@10.6.0: {} + empathic@2.0.0: {} + + env-paths@3.0.0: {} + environment@1.1.0: {} escape-string-regexp@4.0.0: {} @@ -946,9 +1902,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.2.0: + eslint@10.2.0(jiti@2.6.1): dependencies: - "@eslint-community/eslint-utils": 4.9.1(eslint@10.2.0) + "@eslint-community/eslint-utils": 4.9.1(eslint@10.2.0(jiti@2.6.1)) "@eslint-community/regexpp": 4.12.2 "@eslint/config-array": 0.23.4 "@eslint/config-helpers": 0.5.4 @@ -978,6 +1934,8 @@ snapshots: minimatch: 10.2.5 natural-compare: 1.4.0 optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 transitivePeerDependencies: - supports-color @@ -1001,12 +1959,20 @@ snapshots: eventemitter3@5.0.4: {} + exsolve@1.0.8: {} + + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 + fast-deep-equal@3.1.3: {} fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -1023,16 +1989,50 @@ snapshots: flatted@3.4.2: {} + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + get-east-asian-width@1.5.0: {} + get-port-please@3.2.0: {} + + giget@2.0.0: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + defu: 6.1.6 + node-fetch-native: 1.6.7 + nypm: 0.6.5 + pathe: 2.0.3 + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 globals@17.4.0: {} + graceful-fs@4.2.11: {} + + grammex@3.1.12: {} + + graphmatch@1.1.1: {} + + hono@4.12.10: {} + + http-status-codes@2.3.0: {} + husky@9.1.7: {} + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + ignore@5.3.2: {} imurmurhash@0.1.4: {} @@ -1047,12 +2047,18 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-property@1.0.2: {} + isexe@2.0.0: {} + jiti@2.6.1: {} + json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} keyv@4.5.4: @@ -1094,6 +2100,10 @@ snapshots: strip-ansi: 7.2.0 wrap-ansi: 9.0.2 + long@5.3.2: {} + + lru.min@1.1.4: {} + mimic-function@5.0.1: {} minimatch@10.2.5: @@ -1102,8 +2112,34 @@ snapshots: ms@2.1.3: {} + mysql2@3.15.3: + dependencies: + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.7.2 + long: 5.3.2 + lru.min: 1.1.4 + named-placeholders: 1.1.6 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + + named-placeholders@1.1.6: + dependencies: + lru.min: 1.1.4 + natural-compare@1.4.0: {} + node-fetch-native@1.6.7: {} + + nypm@0.6.5: + dependencies: + citty: 0.2.2 + pathe: 2.0.3 + tinyexec: 1.0.4 + + ohash@2.0.11: {} + onetime@7.0.0: dependencies: mimic-function: 5.0.1 @@ -1129,29 +2165,96 @@ snapshots: path-key@3.1.1: {} + pathe@2.0.3: {} + + perfect-debounce@1.0.0: {} + + picocolors@1.1.1: {} + picomatch@4.0.4: {} + pkg-types@2.3.0: + dependencies: + confbox: 0.2.4 + exsolve: 1.0.8 + pathe: 2.0.3 + + postgres@3.4.7: {} + prelude-ls@1.2.1: {} prettier@3.8.1: {} + prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + "@prisma/config": 7.6.0 + "@prisma/dev": 0.24.3 + "@prisma/engines": 7.6.0 + "@prisma/studio-core": 0.27.3(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + mysql2: 3.15.3 + postgres: 3.4.7 + transitivePeerDependencies: + - "@types/react" + - "@types/react-dom" + - magicast + - react + - react-dom + + proper-lockfile@4.1.2: + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + punycode@2.3.1: {} + pure-rand@6.1.0: {} + + rc9@2.1.2: + dependencies: + defu: 6.1.6 + destr: 2.0.5 + + react-dom@19.2.4(react@19.2.4): + dependencies: + react: 19.2.4 + scheduler: 0.27.0 + + react@19.2.4: {} + + readdirp@4.1.2: {} + + remeda@2.33.4: {} + + require-from-string@2.0.2: {} + restore-cursor@5.1.0: dependencies: onetime: 7.0.0 signal-exit: 4.1.0 + retry@0.12.0: {} + rfdc@1.4.1: {} + safer-buffer@2.1.2: {} + + scheduler@0.27.0: {} + + seq-queue@0.0.5: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} + signal-exit@3.0.7: {} + signal-exit@4.1.0: {} + sisteransi@1.0.5: {} + slice-ansi@7.1.2: dependencies: ansi-styles: 6.2.3 @@ -1162,6 +2265,10 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 + sqlstring@2.3.3: {} + + std-env@3.10.0: {} + string-argv@0.3.2: {} string-width@7.2.0: @@ -1198,6 +2305,8 @@ snapshots: dependencies: punycode: 2.3.1 + valibot@1.2.0: {} + which@2.0.2: dependencies: isexe: 2.0.0 @@ -1213,3 +2322,8 @@ snapshots: yaml@2.8.3: {} yocto-queue@0.1.0: {} + + zeptomatch@2.1.0: + dependencies: + grammex: 3.1.12 + graphmatch: 1.1.1 From ae70bd2d75d16cd56d2ace6b47934a9760663fc0 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 12:59:42 +0800 Subject: [PATCH 08/22] feat(api-db): add user auth and security models --- apps/api/prisma/schema.prisma | 72 +++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index 8bddec1..a9d3b67 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -11,3 +11,75 @@ generator client { datasource db { provider = "postgresql" } + +enum UserStatus { + ACTIVE + DISABLED + BANNED +} + +enum AuthProvider { + EMAIL + GITHUB + QQ + WECHAT +} + +model User { + id String @id @default(cuid()) + email String @unique + nickname String? + avatarUrl String? + status UserStatus @default(ACTIVE) + defaultStorageQuotaMb Int @default(100) + usedStorageBytes BigInt @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + identities AuthIdentity[] + refreshTokens RefreshToken[] + security UserSecurity? + + @@map("users") +} + +model AuthIdentity { + id String @id @default(cuid()) + userId String + provider AuthProvider + providerUserId String + email String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([provider, providerUserId]) + @@index([userId]) + @@map("auth_identities") +} + +model UserSecurity { + id String @id @default(cuid()) + userId String @unique + twoFactorEnabled Boolean @default(false) + twoFactorSecret String? + recoveryCodes String[] @default([]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map("user_security") +} + +model RefreshToken { + id String @id @default(cuid()) + userId String + tokenHash String @unique + deviceId String? + expiresAt DateTime + revokedAt DateTime? + createdAt DateTime @default(now()) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId, expiresAt]) + @@map("refresh_tokens") +} From 96cbb8ab302aaa63b7be5000a225b024edbfb3ab Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 13:01:39 +0800 Subject: [PATCH 09/22] feat(api-db): add task attachment and sync models --- apps/api/prisma/schema.prisma | 179 +++++++++++++++++++++++++++++++--- 1 file changed, 168 insertions(+), 11 deletions(-) diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index a9d3b67..1c02f47 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -1,7 +1,4 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -// Get a free hosted Postgres database in seconds: `npx create-db` +// Prisma 数据模型定义(TodoList) generator client { provider = "prisma-client" @@ -25,19 +22,47 @@ enum AuthProvider { WECHAT } +enum TaskPriority { + LOW + MEDIUM + HIGH + URGENT +} + +enum TaskStatus { + TODO + IN_PROGRESS + DONE + ARCHIVED +} + +enum AttachmentType { + IMAGE + VIDEO + FILE + LINK +} + model User { - id String @id @default(cuid()) - email String @unique + id String @id @default(cuid()) + email String @unique nickname String? avatarUrl String? - status UserStatus @default(ACTIVE) - defaultStorageQuotaMb Int @default(100) - usedStorageBytes BigInt @default(0) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + status UserStatus @default(ACTIVE) + defaultStorageQuotaMb Int @default(100) + usedStorageBytes BigInt @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt identities AuthIdentity[] refreshTokens RefreshToken[] security UserSecurity? + tasks Task[] + tags Tag[] + attachments Attachment[] + taskActivityLogs TaskActivityLog[] + syncOperations SyncOperation[] + syncCursors SyncCursor[] + taskTombstones TaskTombstone[] @@map("users") } @@ -83,3 +108,135 @@ model RefreshToken { @@index([userId, expiresAt]) @@map("refresh_tokens") } + +model Task { + id String @id @default(cuid()) + userId String + title String + contentJson Json? + contentText String? + priority TaskPriority @default(MEDIUM) + status TaskStatus @default(TODO) + ddl DateTime? + completedAt DateTime? + version Int @default(1) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + taskTags TaskTag[] + attachments Attachment[] + activityLogs TaskActivityLog[] + + @@index([userId, status]) + @@index([userId, ddl]) + @@map("tasks") +} + +model Tag { + id String @id @default(cuid()) + userId String + name String + color String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + taskTags TaskTag[] + + @@unique([userId, name]) + @@index([userId]) + @@map("tags") +} + +model TaskTag { + taskId String + tagId String + createdAt DateTime @default(now()) + task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) + tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade) + + @@id([taskId, tagId]) + @@index([tagId]) + @@map("task_tags") +} + +model Attachment { + id String @id @default(cuid()) + userId String + taskId String? + type AttachmentType + url String + mimeType String? + fileName String? + fileSize Int + width Int? + height Int? + durationMs Int? + checksum String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + task Task? @relation(fields: [taskId], references: [id], onDelete: SetNull) + + @@index([userId]) + @@index([taskId]) + @@map("attachments") +} + +model TaskActivityLog { + id String @id @default(cuid()) + userId String + taskId String + action String + payload Json? + createdAt DateTime @default(now()) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) + + @@index([taskId, createdAt]) + @@index([userId, createdAt]) + @@map("task_activity_logs") +} + +model SyncOperation { + id String @id @default(cuid()) + opId String @unique + userId String + deviceId String + entityType String + entityId String + action String + payload Json? + clientTs DateTime + serverTs DateTime @default(now()) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId, deviceId, serverTs]) + @@index([userId, entityType, entityId]) + @@map("sync_operations") +} + +model SyncCursor { + id String @id @default(cuid()) + userId String + deviceId String + lastPulledAt DateTime? + lastOperationServerTs DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([userId, deviceId]) + @@map("sync_cursors") +} + +model TaskTombstone { + id String @id @default(cuid()) + taskId String @unique + userId String + deletedAt DateTime @default(now()) + deleteOpId String? + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId, deletedAt]) + @@map("task_tombstones") +} From 011fa1f301524a5453de893285507f99558821bf Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 13:04:21 +0800 Subject: [PATCH 10/22] feat(api-db): add ai notification and admin models --- apps/api/prisma.config.ts | 3 +- apps/api/prisma/schema.prisma | 207 ++++++++++++++++++++++++++++++---- 2 files changed, 185 insertions(+), 25 deletions(-) diff --git a/apps/api/prisma.config.ts b/apps/api/prisma.config.ts index 8f3a0eb..d2c3b82 100644 --- a/apps/api/prisma.config.ts +++ b/apps/api/prisma.config.ts @@ -1,5 +1,4 @@ -// This file was generated by Prisma, and assumes you have installed the following: -// npm install --save-dev prisma dotenv +// Prisma CLI 配置(TodoList) import "dotenv/config"; import { defineConfig } from "prisma/config"; diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index 1c02f47..838df48 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -43,16 +43,34 @@ enum AttachmentType { LINK } +enum AiChannel { + USER_KEY + ASTRBOT + PUBLIC_POOL +} + +enum NotificationChannel { + EMAIL + WEB_PUSH +} + +enum NotificationStatus { + PENDING + SENT + FAILED + CANCELED +} + model User { - id String @id @default(cuid()) - email String @unique + id String @id @default(cuid()) + email String @unique nickname String? avatarUrl String? - status UserStatus @default(ACTIVE) - defaultStorageQuotaMb Int @default(100) - usedStorageBytes BigInt @default(0) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + status UserStatus @default(ACTIVE) + defaultStorageQuotaMb Int @default(100) + usedStorageBytes BigInt @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt identities AuthIdentity[] refreshTokens RefreshToken[] security UserSecurity? @@ -63,6 +81,12 @@ model User { syncOperations SyncOperation[] syncCursors SyncCursor[] taskTombstones TaskTombstone[] + aiProviderBindings AiProviderBinding[] + aiUsageLogs AiUsageLog[] + notificationRules NotificationRule[] + notificationJobs NotificationJob[] + createdAdminTokens AdminToken[] + auditLogs AuditLog[] @@map("users") } @@ -110,22 +134,24 @@ model RefreshToken { } model Task { - id String @id @default(cuid()) - userId String - title String - contentJson Json? - contentText String? - priority TaskPriority @default(MEDIUM) - status TaskStatus @default(TODO) - ddl DateTime? - completedAt DateTime? - version Int @default(1) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - taskTags TaskTag[] - attachments Attachment[] - activityLogs TaskActivityLog[] + id String @id @default(cuid()) + userId String + title String + contentJson Json? + contentText String? + priority TaskPriority @default(MEDIUM) + status TaskStatus @default(TODO) + ddl DateTime? + completedAt DateTime? + version Int @default(1) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + taskTags TaskTag[] + attachments Attachment[] + activityLogs TaskActivityLog[] + notificationJobs NotificationJob[] + notificationRules NotificationRule[] @@index([userId, status]) @@index([userId, ddl]) @@ -240,3 +266,138 @@ model TaskTombstone { @@index([userId, deletedAt]) @@map("task_tombstones") } + +model AiProviderBinding { + id String @id @default(cuid()) + userId String + channel AiChannel + providerName String + model String? + encryptedApiKey String? + endpoint String? + isDefault Boolean @default(false) + isEnabled Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId, isEnabled]) + @@map("ai_provider_bindings") +} + +model AiPublicPoolConfig { + id String @id @default(cuid()) + enabled Boolean @default(false) + providerName String? + model String? + encryptedApiKey String? + endpoint String? + rpmLimit Int @default(60) + dailyTokenLimit Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@map("ai_public_pool_config") +} + +model AiUsageLog { + id String @id @default(cuid()) + userId String? + channel AiChannel + providerName String? + model String? + promptTokens Int @default(0) + completionTokens Int @default(0) + totalTokens Int @default(0) + latencyMs Int? + success Boolean @default(true) + errorCode String? + createdAt DateTime @default(now()) + user User? @relation(fields: [userId], references: [id], onDelete: SetNull) + + @@index([userId, createdAt]) + @@index([channel, createdAt]) + @@map("ai_usage_logs") +} + +model NotificationRule { + id String @id @default(cuid()) + userId String + taskId String? + channel NotificationChannel @default(EMAIL) + advanceMinutes Int @default(60) + enabled Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + task Task? @relation(fields: [taskId], references: [id], onDelete: SetNull) + jobs NotificationJob[] + + @@index([userId, enabled]) + @@index([taskId]) + @@map("notification_rules") +} + +model NotificationJob { + id String @id @default(cuid()) + userId String + taskId String? + ruleId String? + channel NotificationChannel + scheduledAt DateTime + sentAt DateTime? + status NotificationStatus @default(PENDING) + retryCount Int @default(0) + errorMessage String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + task Task? @relation(fields: [taskId], references: [id], onDelete: SetNull) + rule NotificationRule? @relation(fields: [ruleId], references: [id], onDelete: SetNull) + + @@index([status, scheduledAt]) + @@index([userId, createdAt]) + @@map("notification_jobs") +} + +model SystemSetting { + id String @id @default(cuid()) + key String @unique + value Json + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@map("system_settings") +} + +model AdminToken { + id String @id @default(cuid()) + tokenHash String @unique + name String + expiresAt DateTime + lastUsedAt DateTime? + revokedAt DateTime? + createdAt DateTime @default(now()) + createdByUserId String? + createdByUser User? @relation(fields: [createdByUserId], references: [id], onDelete: SetNull) + + @@index([expiresAt]) + @@map("admin_tokens") +} + +model AuditLog { + id String @id @default(cuid()) + actorUserId String? + action String + targetType String + targetId String? + meta Json? + ip String? + userAgent String? + createdAt DateTime @default(now()) + actorUser User? @relation(fields: [actorUserId], references: [id], onDelete: SetNull) + + @@index([action, createdAt]) + @@index([actorUserId, createdAt]) + @@map("audit_logs") +} From 9bd5474401b32e1e2744ca02d35df70bad60dcf9 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 13:06:07 +0800 Subject: [PATCH 11/22] chore(api-db): localize prisma ignore comment --- apps/api/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/.gitignore b/apps/api/.gitignore index 9f62ec0..b4c678e 100644 --- a/apps/api/.gitignore +++ b/apps/api/.gitignore @@ -1,5 +1,5 @@ node_modules -# Keep environment variables out of version control +# 环境变量文件不纳入版本控制 .env /generated/prisma From 5d650e00f6e1d23c34c704e3b3b70688c2d63832 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 14:32:55 +0800 Subject: [PATCH 12/22] fix(api-db): add node type definitions for prisma config --- apps/api/package.json | 1 + pnpm-lock.yaml | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/apps/api/package.json b/apps/api/package.json index fd3daf9..900605c 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -9,6 +9,7 @@ }, "license": "GPL-3.0-or-later", "devDependencies": { + "@types/node": "^25.5.2", "dotenv": "^16.6.1", "prisma": "^7.6.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48e6d7f..7e2a3fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: specifier: ^7.6.0 version: 7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) devDependencies: + "@types/node": + specifier: ^25.5.2 + version: 25.5.2 dotenv: specifier: ^16.6.1 version: 16.6.1 @@ -457,6 +460,12 @@ packages: integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== } + "@types/node@25.5.2": + resolution: + { + integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg== + } + "@types/react@19.2.14": resolution: { @@ -1464,6 +1473,12 @@ packages: } engines: { node: ">= 0.8.0" } + undici-types@7.18.2: + resolution: + { + integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w== + } + uri-js@4.4.1: resolution: { @@ -1762,6 +1777,10 @@ snapshots: "@types/json-schema@7.0.15": {} + "@types/node@25.5.2": + dependencies: + undici-types: 7.18.2 + "@types/react@19.2.14": dependencies: csstype: 3.2.3 @@ -2301,6 +2320,8 @@ snapshots: dependencies: prelude-ls: 1.2.1 + undici-types@7.18.2: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 From efe55fdc2f40e81722d2fb97e4c546745822499f Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 21:02:17 +0800 Subject: [PATCH 13/22] feat(api-auth): implement email code send and login --- apps/api/.env.example | 5 +- apps/api/.gitignore | 3 + apps/api/package.json | 23 +- apps/api/src/app.module.ts | 14 + apps/api/src/auth/auth.controller.ts | 28 + apps/api/src/auth/auth.module.ts | 27 + apps/api/src/auth/auth.service.ts | 104 + apps/api/src/auth/dto/email-login.dto.ts | 11 + apps/api/src/auth/dto/send-email-code.dto.ts | 6 + apps/api/src/main.ts | 19 + apps/api/tsconfig.build.json | 5 + apps/api/tsconfig.json | 10 + pnpm-lock.yaml | 2040 +++++++++++++++++- 13 files changed, 2282 insertions(+), 13 deletions(-) create mode 100644 apps/api/src/app.module.ts create mode 100644 apps/api/src/auth/auth.controller.ts create mode 100644 apps/api/src/auth/auth.module.ts create mode 100644 apps/api/src/auth/auth.service.ts create mode 100644 apps/api/src/auth/dto/email-login.dto.ts create mode 100644 apps/api/src/auth/dto/send-email-code.dto.ts create mode 100644 apps/api/src/main.ts create mode 100644 apps/api/tsconfig.build.json create mode 100644 apps/api/tsconfig.json diff --git a/apps/api/.env.example b/apps/api/.env.example index 203f3a0..6406c52 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -1 +1,4 @@ -DATABASE_URL="postgresql://postgres:postgres@localhost:5432/todolist?schema=public" +DATABASE_URL="postgresql://postgres:postgres@localhost:5432/todolist?schema=public" +AUTH_ACCESS_SECRET="dev-access-secret" +AUTH_ACCESS_EXPIRES_IN_SECONDS="900" +AUTH_EMAIL_CODE_TTL_SECONDS="300" diff --git a/apps/api/.gitignore b/apps/api/.gitignore index b4c678e..13b7b20 100644 --- a/apps/api/.gitignore +++ b/apps/api/.gitignore @@ -3,3 +3,6 @@ node_modules .env /generated/prisma +dist +prisma.config.js +prisma.config.js.map diff --git a/apps/api/package.json b/apps/api/package.json index 900605c..e02e42d 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -5,16 +5,33 @@ "scripts": { "prisma:generate": "prisma generate", "prisma:format": "prisma format", - "prisma:validate": "prisma validate" + "prisma:validate": "prisma validate", + "start": "node dist/main.js", + "start:dev": "ts-node-dev --respawn --transpile-only src/main.ts", + "build": "tsc -p tsconfig.build.json", + "typecheck": "tsc --noEmit -p tsconfig.json", + "test": "echo api tests pending" }, "license": "GPL-3.0-or-later", "devDependencies": { "@types/node": "^25.5.2", "dotenv": "^16.6.1", - "prisma": "^7.6.0" + "prisma": "^7.6.0", + "ts-node": "^10.9.2", + "ts-node-dev": "^2.0.0", + "typescript": "^5.9.3" }, "private": true, "dependencies": { - "@prisma/client": "^7.6.0" + "@nestjs/common": "^11.1.18", + "@nestjs/config": "^4.0.3", + "@nestjs/core": "^11.1.18", + "@nestjs/jwt": "^11.0.2", + "@nestjs/platform-express": "^11.1.18", + "@prisma/client": "^7.6.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.15.1", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.2" } } diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts new file mode 100644 index 0000000..8cd6181 --- /dev/null +++ b/apps/api/src/app.module.ts @@ -0,0 +1,14 @@ +import { Module } from "@nestjs/common"; +import { ConfigModule } from "@nestjs/config"; +import { AuthModule } from "./auth/auth.module"; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + envFilePath: ".env" + }), + AuthModule + ] +}) +export class AppModule {} diff --git a/apps/api/src/auth/auth.controller.ts b/apps/api/src/auth/auth.controller.ts new file mode 100644 index 0000000..6708a53 --- /dev/null +++ b/apps/api/src/auth/auth.controller.ts @@ -0,0 +1,28 @@ +import { Body, Controller, Post } from "@nestjs/common"; +import { AuthService } from "./auth.service"; +import { EmailLoginDto } from "./dto/email-login.dto"; +import { SendEmailCodeDto } from "./dto/send-email-code.dto"; + +@Controller("auth") +export class AuthController { + constructor(private readonly authService: AuthService) {} + + @Post("email/send-code") + async sendEmailCode( + @Body() body: SendEmailCodeDto + ): Promise<{ success: boolean; expiresInSeconds: number; debugCode: string }> { + return this.authService.sendEmailCode(body.email); + } + + @Post("email/login") + async loginWithEmailCode( + @Body() body: EmailLoginDto + ): Promise<{ + accessToken: string; + tokenType: "Bearer"; + expiresInSeconds: number; + user: { id: string; email: string }; + }> { + return this.authService.loginWithEmailCode(body.email, body.code); + } +} diff --git a/apps/api/src/auth/auth.module.ts b/apps/api/src/auth/auth.module.ts new file mode 100644 index 0000000..f8b73ce --- /dev/null +++ b/apps/api/src/auth/auth.module.ts @@ -0,0 +1,27 @@ +import { Module } from "@nestjs/common"; +import { ConfigModule, ConfigService } from "@nestjs/config"; +import { JwtModule } from "@nestjs/jwt"; +import { AuthController } from "./auth.controller"; +import { AuthService } from "./auth.service"; + +@Module({ + imports: [ + ConfigModule, + JwtModule.registerAsync({ + inject: [ConfigService], + useFactory: (configService: ConfigService) => { + const expiresInSeconds = Number(configService.get("AUTH_ACCESS_EXPIRES_IN_SECONDS") ?? 900); + + return { + secret: configService.get("AUTH_ACCESS_SECRET") ?? "dev-access-secret", + signOptions: { + expiresIn: expiresInSeconds + } + }; + } + }) + ], + controllers: [AuthController], + providers: [AuthService] +}) +export class AuthModule {} diff --git a/apps/api/src/auth/auth.service.ts b/apps/api/src/auth/auth.service.ts new file mode 100644 index 0000000..9ede99c --- /dev/null +++ b/apps/api/src/auth/auth.service.ts @@ -0,0 +1,104 @@ +import { Injectable, UnauthorizedException } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { JwtService } from "@nestjs/jwt"; +import { randomUUID } from "node:crypto"; + +type EmailCodeEntry = { + code: string; + expiresAt: number; +}; + +type AuthUser = { + id: string; + email: string; +}; + +@Injectable() +export class AuthService { + private readonly emailCodeStore = new Map(); + private readonly userStore = new Map(); + + constructor( + private readonly configService: ConfigService, + private readonly jwtService: JwtService + ) {} + + async sendEmailCode( + email: string + ): Promise<{ success: boolean; expiresInSeconds: number; debugCode: string }> { + const ttlSeconds = Number(this.configService.get("AUTH_EMAIL_CODE_TTL_SECONDS") ?? 300); + const code = this.generateCode(); + const expiresAt = Date.now() + ttlSeconds * 1000; + + this.emailCodeStore.set(email.toLowerCase(), { code, expiresAt }); + + return { + success: true, + expiresInSeconds: ttlSeconds, + debugCode: code + }; + } + + async loginWithEmailCode( + email: string, + code: string + ): Promise<{ + accessToken: string; + tokenType: "Bearer"; + expiresInSeconds: number; + user: AuthUser; + }> { + const lowerEmail = email.toLowerCase(); + const codeEntry = this.emailCodeStore.get(lowerEmail); + + if (!codeEntry) { + throw new UnauthorizedException("验证码不存在或已失效"); + } + + if (codeEntry.expiresAt < Date.now()) { + this.emailCodeStore.delete(lowerEmail); + throw new UnauthorizedException("验证码已过期"); + } + + if (codeEntry.code !== code) { + throw new UnauthorizedException("验证码错误"); + } + + this.emailCodeStore.delete(lowerEmail); + + const user = this.getOrCreateUser(lowerEmail); + const expiresInSeconds = Number( + this.configService.get("AUTH_ACCESS_EXPIRES_IN_SECONDS") ?? 900 + ); + const accessToken = await this.jwtService.signAsync({ + sub: user.id, + email: user.email + }); + + return { + accessToken, + tokenType: "Bearer", + expiresInSeconds, + user + }; + } + + private getOrCreateUser(email: string): AuthUser { + const existingUser = this.userStore.get(email); + if (existingUser) { + return existingUser; + } + + const newUser = { + id: randomUUID(), + email + }; + this.userStore.set(email, newUser); + + return newUser; + } + + private generateCode(): string { + return String(Math.floor(100000 + Math.random() * 900000)); + } +} diff --git a/apps/api/src/auth/dto/email-login.dto.ts b/apps/api/src/auth/dto/email-login.dto.ts new file mode 100644 index 0000000..32b1511 --- /dev/null +++ b/apps/api/src/auth/dto/email-login.dto.ts @@ -0,0 +1,11 @@ +import { IsEmail, IsString, Length, Matches } from "class-validator"; + +export class EmailLoginDto { + @IsEmail() + email!: string; + + @IsString() + @Length(6, 6) + @Matches(/^\d{6}$/) + code!: string; +} diff --git a/apps/api/src/auth/dto/send-email-code.dto.ts b/apps/api/src/auth/dto/send-email-code.dto.ts new file mode 100644 index 0000000..998b618 --- /dev/null +++ b/apps/api/src/auth/dto/send-email-code.dto.ts @@ -0,0 +1,6 @@ +import { IsEmail } from "class-validator"; + +export class SendEmailCodeDto { + @IsEmail() + email!: string; +} diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts new file mode 100644 index 0000000..c7aef94 --- /dev/null +++ b/apps/api/src/main.ts @@ -0,0 +1,19 @@ +import "reflect-metadata"; +import { ValidationPipe } from "@nestjs/common"; +import { NestFactory } from "@nestjs/core"; +import { AppModule } from "./app.module"; + +async function bootstrap(): Promise { + const app = await NestFactory.create(AppModule); + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + forbidNonWhitelisted: true + }) + ); + + await app.listen(3000); +} + +void bootstrap(); diff --git a/apps/api/tsconfig.build.json b/apps/api/tsconfig.build.json new file mode 100644 index 0000000..10b2c04 --- /dev/null +++ b/apps/api/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.json", + "exclude": ["node_modules", "dist", "**/*.spec.ts"] +} diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json new file mode 100644 index 0000000..a6aded2 --- /dev/null +++ b/apps/api/tsconfig.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../packages/tsconfig/nest-app.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src/**/*.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e2a3fb..6c8607a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,9 +31,36 @@ importers: apps/api: dependencies: + "@nestjs/common": + specifier: ^11.1.18 + version: 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + "@nestjs/config": + specifier: ^4.0.3 + version: 4.0.3(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) + "@nestjs/core": + specifier: ^11.1.18 + version: 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) + "@nestjs/jwt": + specifier: ^11.0.2 + version: 11.0.2(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)) + "@nestjs/platform-express": + specifier: ^11.1.18 + version: 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18) "@prisma/client": specifier: ^7.6.0 - version: 7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + version: 7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) + class-transformer: + specifier: ^0.5.1 + version: 0.5.1 + class-validator: + specifier: ^0.15.1 + version: 0.15.1 + reflect-metadata: + specifier: ^0.2.2 + version: 0.2.2 + rxjs: + specifier: ^7.8.2 + version: 7.8.2 devDependencies: "@types/node": specifier: ^25.5.2 @@ -43,13 +70,28 @@ importers: version: 16.6.1 prisma: specifier: ^7.6.0 - version: 7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@25.5.2)(typescript@5.9.3) + ts-node-dev: + specifier: ^2.0.0 + version: 2.0.0(@types/node@25.5.2)(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 packages/eslint-config: {} packages/tsconfig: {} packages: + "@borewit/text-codec@0.2.2": + resolution: + { + integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ== + } + "@clack/core@0.5.0": resolution: { @@ -62,6 +104,13 @@ packages: integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw== } + "@cspotcode/source-map-support@0.8.1": + resolution: + { + integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + } + engines: { node: ">=12" } + "@electric-sql/pglite-socket@0.1.1": resolution: { @@ -185,12 +234,109 @@ packages: } engines: { node: ">=18.18" } + "@jridgewell/resolve-uri@3.1.2": + resolution: + { + integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + } + engines: { node: ">=6.0.0" } + + "@jridgewell/sourcemap-codec@1.5.5": + resolution: + { + integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + } + + "@jridgewell/trace-mapping@0.3.9": + resolution: + { + integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + } + "@kurkle/color@0.3.4": resolution: { integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w== } + "@lukeed/csprng@1.1.0": + resolution: + { + integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA== + } + engines: { node: ">=8" } + + "@nestjs/common@11.1.18": + resolution: + { + integrity: sha512-0sLq8Z+TIjLnz1Tqp0C/x9BpLbqpt1qEu0VcH4/fkE0y3F5JxhfK1AdKQ/SPbKhKgwqVDoY4gS8GQr2G6ujaWg== + } + peerDependencies: + class-transformer: ">=0.4.1" + class-validator: ">=0.13.2" + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + + "@nestjs/config@4.0.3": + resolution: + { + integrity: sha512-FQ3M3Ohqfl+nHAn5tp7++wUQw0f2nAk+SFKe8EpNRnIifPqvfJP6JQxPKtFLMOHbyer4X646prFG4zSRYEssQQ== + } + peerDependencies: + "@nestjs/common": ^10.0.0 || ^11.0.0 + rxjs: ^7.1.0 + + "@nestjs/core@11.1.18": + resolution: + { + integrity: sha512-wR3DtGyk/LUAiPtbXDuWJJwVkWElKBY0sqnTzf9d4uM3+X18FRZhK7WFc47czsIGOdWuRsMeLYV+1Z9dO4zDEQ== + } + engines: { node: ">= 20" } + peerDependencies: + "@nestjs/common": ^11.0.0 + "@nestjs/microservices": ^11.0.0 + "@nestjs/platform-express": ^11.0.0 + "@nestjs/websockets": ^11.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + "@nestjs/microservices": + optional: true + "@nestjs/platform-express": + optional: true + "@nestjs/websockets": + optional: true + + "@nestjs/jwt@11.0.2": + resolution: + { + integrity: sha512-rK8aE/3/Ma45gAWfCksAXUNbOoSOUudU0Kn3rT39htPF7wsYXtKfjALKeKKJbFrIWbLjsbqfXX5bIJNvgBugGA== + } + peerDependencies: + "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + + "@nestjs/platform-express@11.1.18": + resolution: + { + integrity: sha512-s6GdHMTa3qx0fJewR74Xa30ysPHfBEqxIwZ7BGSTLoAEQ1vTP24urNl+b6+s49NFLEIOyeNho5fN/9/I17QlOw== + } + peerDependencies: + "@nestjs/common": ^11.0.0 + "@nestjs/core": ^11.0.0 + + "@nuxt/opencollective@0.4.1": + resolution: + { + integrity: sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ== + } + engines: { node: ^14.18.0 || >=16.10.0, npm: ">=5.10.0" } + hasBin: true + "@prisma/client-runtime-utils@7.6.0": resolution: { @@ -394,6 +540,43 @@ packages: integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== } + "@tokenizer/inflate@0.4.1": + resolution: + { + integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA== + } + engines: { node: ">=18" } + + "@tokenizer/token@0.3.0": + resolution: + { + integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== + } + + "@tsconfig/node10@1.0.12": + resolution: + { + integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ== + } + + "@tsconfig/node12@1.0.11": + resolution: + { + integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + } + + "@tsconfig/node14@1.0.3": + resolution: + { + integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + } + + "@tsconfig/node16@1.0.4": + resolution: + { + integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + } + "@turbo/darwin-64@2.9.3": resolution: { @@ -460,6 +643,18 @@ packages: integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== } + "@types/jsonwebtoken@9.0.10": + resolution: + { + integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA== + } + + "@types/ms@2.1.0": + resolution: + { + integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== + } + "@types/node@25.5.2": resolution: { @@ -472,6 +667,31 @@ packages: integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w== } + "@types/strip-bom@3.0.0": + resolution: + { + integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ== + } + + "@types/strip-json-comments@0.0.30": + resolution: + { + integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== + } + + "@types/validator@13.15.10": + resolution: + { + integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA== + } + + accepts@2.0.0: + resolution: + { + integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + } + engines: { node: ">= 0.6" } + acorn-jsx@5.3.2: resolution: { @@ -480,6 +700,13 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-walk@8.3.5: + resolution: + { + integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw== + } + engines: { node: ">=0.4.0" } + acorn@8.16.0: resolution: { @@ -521,6 +748,25 @@ packages: } engines: { node: ">=12" } + anymatch@3.1.3: + resolution: + { + integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + } + engines: { node: ">= 8" } + + append-field@1.0.0: + resolution: + { + integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== + } + + arg@4.1.3: + resolution: + { + integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + } + aws-ssl-profiles@1.1.2: resolution: { @@ -528,6 +774,12 @@ packages: } engines: { node: ">= 6.0.0" } + balanced-match@1.0.2: + resolution: + { + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + } + balanced-match@4.0.4: resolution: { @@ -542,6 +794,26 @@ packages: } hasBin: true + binary-extensions@2.3.0: + resolution: + { + integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + } + engines: { node: ">=8" } + + body-parser@2.2.2: + resolution: + { + integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA== + } + engines: { node: ">=18" } + + brace-expansion@1.1.13: + resolution: + { + integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w== + } + brace-expansion@5.0.5: resolution: { @@ -549,6 +821,39 @@ packages: } engines: { node: 18 || 20 || >=22 } + braces@3.0.3: + resolution: + { + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + } + engines: { node: ">=8" } + + buffer-equal-constant-time@1.0.1: + resolution: + { + integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + } + + buffer-from@1.1.2: + resolution: + { + integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + } + + busboy@1.6.0: + resolution: + { + integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + } + engines: { node: ">=10.16.0" } + + bytes@3.1.2: + resolution: + { + integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + } + engines: { node: ">= 0.8" } + c12@3.1.0: resolution: { @@ -560,6 +865,20 @@ packages: magicast: optional: true + call-bind-apply-helpers@1.0.2: + resolution: + { + integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + } + engines: { node: ">= 0.4" } + + call-bound@1.0.4: + resolution: + { + integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + } + engines: { node: ">= 0.4" } + chart.js@4.5.1: resolution: { @@ -567,6 +886,13 @@ packages: } engines: { pnpm: ">=8" } + chokidar@3.6.0: + resolution: + { + integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + } + engines: { node: ">= 8.10.0" } + chokidar@4.0.3: resolution: { @@ -586,6 +912,18 @@ packages: integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w== } + class-transformer@0.5.1: + resolution: + { + integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== + } + + class-validator@0.15.1: + resolution: + { + integrity: sha512-LqoS80HBBSCVhz/3KloUly0ovokxpdOLR++Al3J3+dHXWt9sTKlKd4eYtoxhxyUjoe5+UcIM+5k9MIxyBWnRTw== + } + cli-cursor@5.0.0: resolution: { @@ -613,6 +951,19 @@ packages: } engines: { node: ">=20" } + concat-map@0.0.1: + resolution: + { + integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + } + + concat-stream@2.0.0: + resolution: + { + integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== + } + engines: { "0": node >= 6.0 } + confbox@0.2.4: resolution: { @@ -626,6 +977,47 @@ packages: } engines: { node: ^14.18.0 || >=16.10.0 } + content-disposition@1.0.1: + resolution: + { + integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q== + } + engines: { node: ">=18" } + + content-type@1.0.5: + resolution: + { + integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + } + engines: { node: ">= 0.6" } + + cookie-signature@1.2.2: + resolution: + { + integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + } + engines: { node: ">=6.6.0" } + + cookie@0.7.2: + resolution: + { + integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + } + engines: { node: ">= 0.6" } + + cors@2.8.6: + resolution: + { + integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw== + } + engines: { node: ">= 0.10" } + + create-require@1.1.1: + resolution: + { + integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + } + cross-spawn@7.0.6: resolution: { @@ -677,12 +1069,33 @@ packages: } engines: { node: ">=0.10" } + depd@2.0.0: + resolution: + { + integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + } + engines: { node: ">= 0.8" } + destr@2.0.5: resolution: { integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA== } + diff@4.0.4: + resolution: + { + integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ== + } + engines: { node: ">=0.3.1" } + + dotenv-expand@12.0.3: + resolution: + { + integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA== + } + engines: { node: ">=12" } + dotenv@16.6.1: resolution: { @@ -690,6 +1103,38 @@ packages: } engines: { node: ">=12" } + dotenv@17.2.3: + resolution: + { + integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w== + } + engines: { node: ">=12" } + + dunder-proto@1.0.1: + resolution: + { + integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + } + engines: { node: ">= 0.4" } + + dynamic-dedupe@0.3.0: + resolution: + { + integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ== + } + + ecdsa-sig-formatter@1.0.11: + resolution: + { + integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + } + + ee-first@1.1.1: + resolution: + { + integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + } + effect@3.20.0: resolution: { @@ -709,6 +1154,13 @@ packages: } engines: { node: ">=14" } + encodeurl@2.0.0: + resolution: + { + integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + } + engines: { node: ">= 0.8" } + env-paths@3.0.0: resolution: { @@ -723,6 +1175,33 @@ packages: } engines: { node: ">=18" } + es-define-property@1.0.1: + resolution: + { + integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + } + engines: { node: ">= 0.4" } + + es-errors@1.3.0: + resolution: + { + integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + } + engines: { node: ">= 0.4" } + + es-object-atoms@1.1.1: + resolution: + { + integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + } + engines: { node: ">= 0.4" } + + escape-html@1.0.3: + resolution: + { + integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + } + escape-string-regexp@4.0.0: resolution: { @@ -799,12 +1278,26 @@ packages: } engines: { node: ">=0.10.0" } + etag@1.8.1: + resolution: + { + integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + } + engines: { node: ">= 0.6" } + eventemitter3@5.0.4: resolution: { integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== } + express@5.2.1: + resolution: + { + integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== + } + engines: { node: ">= 18" } + exsolve@1.0.8: resolution: { @@ -836,6 +1329,12 @@ packages: integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== } + fast-safe-stringify@2.1.1: + resolution: + { + integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + } + fast-uri@3.1.0: resolution: { @@ -849,6 +1348,27 @@ packages: } engines: { node: ">=16.0.0" } + file-type@21.3.4: + resolution: + { + integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g== + } + engines: { node: ">=20" } + + fill-range@7.1.1: + resolution: + { + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + } + engines: { node: ">=8" } + + finalhandler@2.1.1: + resolution: + { + integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== + } + engines: { node: ">= 18.0.0" } + find-up@5.0.0: resolution: { @@ -876,6 +1396,40 @@ packages: } engines: { node: ">=14" } + forwarded@0.2.0: + resolution: + { + integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + } + engines: { node: ">= 0.6" } + + fresh@2.0.0: + resolution: + { + integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + } + engines: { node: ">= 0.8" } + + fs.realpath@1.0.0: + resolution: + { + integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + } + + fsevents@2.3.3: + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + os: [darwin] + + function-bind@1.1.2: + resolution: + { + integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + } + generate-function@2.3.1: resolution: { @@ -889,12 +1443,26 @@ packages: } engines: { node: ">=18" } + get-intrinsic@1.3.0: + resolution: + { + integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + } + engines: { node: ">= 0.4" } + get-port-please@3.2.0: resolution: { integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A== } + get-proto@1.0.1: + resolution: + { + integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + } + engines: { node: ">= 0.4" } + giget@2.0.0: resolution: { @@ -902,6 +1470,13 @@ packages: } hasBin: true + glob-parent@5.1.2: + resolution: + { + integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + } + engines: { node: ">= 6" } + glob-parent@6.0.2: resolution: { @@ -909,6 +1484,13 @@ packages: } engines: { node: ">=10.13.0" } + glob@7.2.3: + resolution: + { + integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + } + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + globals@17.4.0: resolution: { @@ -916,6 +1498,13 @@ packages: } engines: { node: ">=18" } + gopd@1.2.0: + resolution: + { + integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + } + engines: { node: ">= 0.4" } + graceful-fs@4.2.11: resolution: { @@ -934,6 +1523,20 @@ packages: integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg== } + has-symbols@1.1.0: + resolution: + { + integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + } + engines: { node: ">= 0.4" } + + hasown@2.0.2: + resolution: + { + integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + } + engines: { node: ">= 0.4" } + hono@4.12.10: resolution: { @@ -941,6 +1544,13 @@ packages: } engines: { node: ">=16.9.0" } + http-errors@2.0.1: + resolution: + { + integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + } + engines: { node: ">= 0.8" } + http-status-codes@2.3.0: resolution: { @@ -962,6 +1572,12 @@ packages: } engines: { node: ">=0.10.0" } + ieee754@1.2.1: + resolution: + { + integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + } + ignore@5.3.2: resolution: { @@ -976,6 +1592,40 @@ packages: } engines: { node: ">=0.8.19" } + inflight@1.0.6: + resolution: + { + integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + } + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: + { + integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + } + + ipaddr.js@1.9.1: + resolution: + { + integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + } + engines: { node: ">= 0.10" } + + is-binary-path@2.1.0: + resolution: + { + integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + } + engines: { node: ">=8" } + + is-core-module@2.16.1: + resolution: + { + integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + } + engines: { node: ">= 0.4" } + is-extglob@2.1.1: resolution: { @@ -997,6 +1647,19 @@ packages: } engines: { node: ">=0.10.0" } + is-number@7.0.0: + resolution: + { + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + } + engines: { node: ">=0.12.0" } + + is-promise@4.0.0: + resolution: + { + integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + } + is-property@1.0.2: resolution: { @@ -1009,6 +1672,13 @@ packages: integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== } + iterare@1.2.1: + resolution: + { + integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q== + } + engines: { node: ">=6" } + jiti@2.6.1: resolution: { @@ -1040,6 +1710,25 @@ packages: integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== } + jsonwebtoken@9.0.3: + resolution: + { + integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g== + } + engines: { node: ">=12", npm: ">=6" } + + jwa@2.0.1: + resolution: + { + integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg== + } + + jws@4.0.1: + resolution: + { + integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA== + } + keyv@4.5.4: resolution: { @@ -1053,6 +1742,12 @@ packages: } engines: { node: ">= 0.8.0" } + libphonenumber-js@1.12.41: + resolution: + { + integrity: sha512-lsmMmGXBxXIK/VMLEj0kL6MtUs1kBGj1nTCzi6zgQoG1DEwqwt2DQyHxcLykceIxAnfE3hya7NuIh6PpC6S3fA== + } + lint-staged@16.4.0: resolution: { @@ -1068,6 +1763,13 @@ packages: } engines: { node: ">=20.0.0" } + load-esm@1.0.3: + resolution: + { + integrity: sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA== + } + engines: { node: ">=13.2.0" } + locate-path@6.0.0: resolution: { @@ -1075,6 +1777,54 @@ packages: } engines: { node: ">=10" } + lodash.includes@4.3.0: + resolution: + { + integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + } + + lodash.isboolean@3.0.3: + resolution: + { + integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + } + + lodash.isinteger@4.0.4: + resolution: + { + integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + } + + lodash.isnumber@3.0.3: + resolution: + { + integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + } + + lodash.isplainobject@4.0.6: + resolution: + { + integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + } + + lodash.isstring@4.0.1: + resolution: + { + integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + } + + lodash.once@4.1.1: + resolution: + { + integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + } + + lodash@4.17.23: + resolution: + { + integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== + } + log-update@6.1.0: resolution: { @@ -1095,6 +1845,68 @@ packages: } engines: { bun: ">=1.0.0", deno: ">=1.30.0", node: ">=8.0.0" } + make-error@1.3.6: + resolution: + { + integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + } + + math-intrinsics@1.1.0: + resolution: + { + integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + } + engines: { node: ">= 0.4" } + + media-typer@0.3.0: + resolution: + { + integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + } + engines: { node: ">= 0.6" } + + media-typer@1.1.0: + resolution: + { + integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + } + engines: { node: ">= 0.8" } + + merge-descriptors@2.0.0: + resolution: + { + integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + } + engines: { node: ">=18" } + + mime-db@1.52.0: + resolution: + { + integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + } + engines: { node: ">= 0.6" } + + mime-db@1.54.0: + resolution: + { + integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + } + engines: { node: ">= 0.6" } + + mime-types@2.1.35: + resolution: + { + integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + } + engines: { node: ">= 0.6" } + + mime-types@3.0.2: + resolution: + { + integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + } + engines: { node: ">=18" } + mimic-function@5.0.1: resolution: { @@ -1109,12 +1921,39 @@ packages: } engines: { node: 18 || 20 || >=22 } + minimatch@3.1.5: + resolution: + { + integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== + } + + minimist@1.2.8: + resolution: + { + integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + } + + mkdirp@1.0.4: + resolution: + { + integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + } + engines: { node: ">=10" } + hasBin: true + ms@2.1.3: resolution: { integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== } + multer@2.1.1: + resolution: + { + integrity: sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A== + } + engines: { node: ">= 10.16.0" } + mysql2@3.15.3: resolution: { @@ -1135,12 +1974,26 @@ packages: integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== } + negotiator@1.0.0: + resolution: + { + integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + } + engines: { node: ">= 0.6" } + node-fetch-native@1.6.7: resolution: { integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q== } + normalize-path@3.0.0: + resolution: + { + integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + } + engines: { node: ">=0.10.0" } + nypm@0.6.5: resolution: { @@ -1149,12 +2002,39 @@ packages: engines: { node: ">=18" } hasBin: true + object-assign@4.1.1: + resolution: + { + integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + } + engines: { node: ">=0.10.0" } + + object-inspect@1.13.4: + resolution: + { + integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + } + engines: { node: ">= 0.4" } + ohash@2.0.11: resolution: { integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ== } + on-finished@2.4.1: + resolution: + { + integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + } + engines: { node: ">= 0.8" } + + once@1.4.0: + resolution: + { + integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + } + onetime@7.0.0: resolution: { @@ -1183,6 +2063,13 @@ packages: } engines: { node: ">=10" } + parseurl@1.3.3: + resolution: + { + integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + } + engines: { node: ">= 0.8" } + path-exists@4.0.0: resolution: { @@ -1190,6 +2077,13 @@ packages: } engines: { node: ">=8" } + path-is-absolute@1.0.1: + resolution: + { + integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + } + engines: { node: ">=0.10.0" } + path-key@3.1.1: resolution: { @@ -1197,6 +2091,18 @@ packages: } engines: { node: ">=8" } + path-parse@1.0.7: + resolution: + { + integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + } + + path-to-regexp@8.4.2: + resolution: + { + integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA== + } + pathe@2.0.3: resolution: { @@ -1215,6 +2121,13 @@ packages: integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== } + picomatch@2.3.2: + resolution: + { + integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== + } + engines: { node: ">=8.6" } + picomatch@4.0.4: resolution: { @@ -1272,6 +2185,13 @@ packages: integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== } + proxy-addr@2.0.7: + resolution: + { + integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + } + engines: { node: ">= 0.10" } + punycode@2.3.1: resolution: { @@ -1285,6 +2205,27 @@ packages: integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== } + qs@6.15.0: + resolution: + { + integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ== + } + engines: { node: ">=0.6" } + + range-parser@1.2.1: + resolution: + { + integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + } + engines: { node: ">= 0.6" } + + raw-body@3.0.2: + resolution: + { + integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== + } + engines: { node: ">= 0.10" } + rc9@2.1.2: resolution: { @@ -1306,6 +2247,20 @@ packages: } engines: { node: ">=0.10.0" } + readable-stream@3.6.2: + resolution: + { + integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + } + engines: { node: ">= 6" } + + readdirp@3.6.0: + resolution: + { + integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + } + engines: { node: ">=8.10.0" } + readdirp@4.1.2: resolution: { @@ -1313,6 +2268,12 @@ packages: } engines: { node: ">= 14.18.0" } + reflect-metadata@0.2.2: + resolution: + { + integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== + } + remeda@2.33.4: resolution: { @@ -1326,6 +2287,14 @@ packages: } engines: { node: ">=0.10.0" } + resolve@1.22.11: + resolution: + { + integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== + } + engines: { node: ">= 0.4" } + hasBin: true + restore-cursor@5.1.0: resolution: { @@ -1346,6 +2315,33 @@ packages: integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== } + rimraf@2.7.1: + resolution: + { + integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + } + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + router@2.2.0: + resolution: + { + integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + } + engines: { node: ">= 18" } + + rxjs@7.8.2: + resolution: + { + integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== + } + + safe-buffer@5.2.1: + resolution: + { + integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + } + safer-buffer@2.1.2: resolution: { @@ -1358,12 +2354,40 @@ packages: integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== } + semver@7.7.4: + resolution: + { + integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== + } + engines: { node: ">=10" } + hasBin: true + + send@1.2.1: + resolution: + { + integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== + } + engines: { node: ">= 18" } + seq-queue@0.0.5: resolution: { integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q== } + serve-static@2.2.1: + resolution: + { + integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw== + } + engines: { node: ">= 18" } + + setprototypeof@1.2.0: + resolution: + { + integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + } + shebang-command@2.0.0: resolution: { @@ -1378,6 +2402,34 @@ packages: } engines: { node: ">=8" } + side-channel-list@1.0.0: + resolution: + { + integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + } + engines: { node: ">= 0.4" } + + side-channel-map@1.0.1: + resolution: + { + integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + } + engines: { node: ">= 0.4" } + + side-channel-weakmap@1.0.2: + resolution: + { + integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + } + engines: { node: ">= 0.4" } + + side-channel@1.1.0: + resolution: + { + integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + } + engines: { node: ">= 0.4" } + signal-exit@3.0.7: resolution: { @@ -1411,6 +2463,19 @@ packages: } engines: { node: ">=20" } + source-map-support@0.5.21: + resolution: + { + integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + } + + source-map@0.6.1: + resolution: + { + integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + } + engines: { node: ">=0.10.0" } + sqlstring@2.3.3: resolution: { @@ -1418,12 +2483,26 @@ packages: } engines: { node: ">= 0.6" } + statuses@2.0.2: + resolution: + { + integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + } + engines: { node: ">= 0.8" } + std-env@3.10.0: resolution: { integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg== } + streamsearch@1.1.0: + resolution: + { + integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + } + engines: { node: ">=10.0.0" } + string-argv@0.3.2: resolution: { @@ -1445,6 +2524,12 @@ packages: } engines: { node: ">=20" } + string_decoder@1.3.0: + resolution: + { + integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + } + strip-ansi@7.2.0: resolution: { @@ -1452,6 +2537,34 @@ packages: } engines: { node: ">=12" } + strip-bom@3.0.0: + resolution: + { + integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + } + engines: { node: ">=4" } + + strip-json-comments@2.0.1: + resolution: + { + integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + } + engines: { node: ">=0.10.0" } + + strtok3@10.3.5: + resolution: + { + integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA== + } + engines: { node: ">=18" } + + supports-preserve-symlinks-flag@1.0.0: + resolution: + { + integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + } + engines: { node: ">= 0.4" } + tinyexec@1.0.4: resolution: { @@ -1459,6 +2572,77 @@ packages: } engines: { node: ">=18" } + to-regex-range@5.0.1: + resolution: + { + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + } + engines: { node: ">=8.0" } + + toidentifier@1.0.1: + resolution: + { + integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + } + engines: { node: ">=0.6" } + + token-types@6.1.2: + resolution: + { + integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww== + } + engines: { node: ">=14.16" } + + tree-kill@1.2.2: + resolution: + { + integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + } + hasBin: true + + ts-node-dev@2.0.0: + resolution: + { + integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w== + } + engines: { node: ">=0.8.0" } + hasBin: true + peerDependencies: + node-notifier: "*" + typescript: "*" + peerDependenciesMeta: + node-notifier: + optional: true + + ts-node@10.9.2: + resolution: + { + integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + } + hasBin: true + peerDependencies: + "@swc/core": ">=1.2.50" + "@swc/wasm": ">=1.2.50" + "@types/node": "*" + typescript: ">=2.7" + peerDependenciesMeta: + "@swc/core": + optional: true + "@swc/wasm": + optional: true + + tsconfig@7.0.0: + resolution: + { + integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== + } + + tslib@2.8.1: + resolution: + { + integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + } + turbo@2.9.3: resolution: { @@ -1473,18 +2657,79 @@ packages: } engines: { node: ">= 0.8.0" } + type-is@1.6.18: + resolution: + { + integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + } + engines: { node: ">= 0.6" } + + type-is@2.0.1: + resolution: + { + integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + } + engines: { node: ">= 0.6" } + + typedarray@0.0.6: + resolution: + { + integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + } + + typescript@5.9.3: + resolution: + { + integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== + } + engines: { node: ">=14.17" } + hasBin: true + + uid@2.0.2: + resolution: + { + integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g== + } + engines: { node: ">=8" } + + uint8array-extras@1.5.0: + resolution: + { + integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A== + } + engines: { node: ">=18" } + undici-types@7.18.2: resolution: { integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w== } + unpipe@1.0.0: + resolution: + { + integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + } + engines: { node: ">= 0.8" } + uri-js@4.4.1: resolution: { integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== } + util-deprecate@1.0.2: + resolution: + { + integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + } + + v8-compile-cache-lib@3.0.1: + resolution: + { + integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + } + valibot@1.2.0: resolution: { @@ -1496,6 +2741,20 @@ packages: typescript: optional: true + validator@13.15.35: + resolution: + { + integrity: sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw== + } + engines: { node: ">= 0.10" } + + vary@1.1.2: + resolution: + { + integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + } + engines: { node: ">= 0.8" } + which@2.0.2: resolution: { @@ -1518,6 +2777,19 @@ packages: } engines: { node: ">=18" } + wrappy@1.0.2: + resolution: + { + integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + } + + xtend@4.0.2: + resolution: + { + integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + } + engines: { node: ">=0.4" } + yaml@2.8.3: resolution: { @@ -1526,6 +2798,13 @@ packages: engines: { node: ">= 14.6" } hasBin: true + yn@3.1.1: + resolution: + { + integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + } + engines: { node: ">=6" } + yocto-queue@0.1.0: resolution: { @@ -1540,6 +2819,8 @@ packages: } snapshots: + "@borewit/text-codec@0.2.2": {} + "@clack/core@0.5.0": dependencies: picocolors: 1.1.1 @@ -1551,6 +2832,10 @@ snapshots: picocolors: 1.1.1 sisteransi: 1.0.5 + "@cspotcode/source-map-support@0.8.1": + dependencies: + "@jridgewell/trace-mapping": 0.3.9 + "@electric-sql/pglite-socket@0.1.1(@electric-sql/pglite@0.4.1)": dependencies: "@electric-sql/pglite": 0.4.1 @@ -1610,15 +2895,86 @@ snapshots: "@humanwhocodes/retry@0.4.3": {} + "@jridgewell/resolve-uri@3.1.2": {} + + "@jridgewell/sourcemap-codec@1.5.5": {} + + "@jridgewell/trace-mapping@0.3.9": + dependencies: + "@jridgewell/resolve-uri": 3.1.2 + "@jridgewell/sourcemap-codec": 1.5.5 + "@kurkle/color@0.3.4": {} + "@lukeed/csprng@1.1.0": {} + + "@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)": + dependencies: + file-type: 21.3.4 + iterare: 1.2.1 + load-esm: 1.0.3 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + optionalDependencies: + class-transformer: 0.5.1 + class-validator: 0.15.1 + transitivePeerDependencies: + - supports-color + + "@nestjs/config@4.0.3(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)": + dependencies: + "@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + dotenv: 17.2.3 + dotenv-expand: 12.0.3 + lodash: 4.17.23 + rxjs: 7.8.2 + + "@nestjs/core@11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2)": + dependencies: + "@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + "@nuxt/opencollective": 0.4.1 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 8.4.2 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + optionalDependencies: + "@nestjs/platform-express": 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18) + + "@nestjs/jwt@11.0.2(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))": + dependencies: + "@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + "@types/jsonwebtoken": 9.0.10 + jsonwebtoken: 9.0.3 + + "@nestjs/platform-express@11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)": + dependencies: + "@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + "@nestjs/core": 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) + cors: 2.8.6 + express: 5.2.1 + multer: 2.1.1 + path-to-regexp: 8.4.2 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + "@nuxt/opencollective@0.4.1": + dependencies: + consola: 3.4.2 + "@prisma/client-runtime-utils@7.6.0": {} - "@prisma/client@7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))": + "@prisma/client@7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)": dependencies: "@prisma/client-runtime-utils": 7.6.0 optionalDependencies: - prisma: 7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + prisma: 7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + typescript: 5.9.3 "@prisma/config@7.6.0": dependencies: @@ -1633,7 +2989,7 @@ snapshots: "@prisma/debug@7.6.0": {} - "@prisma/dev@0.24.3": + "@prisma/dev@0.24.3(typescript@5.9.3)": dependencies: "@electric-sql/pglite": 0.4.1 "@electric-sql/pglite-socket": 0.1.1(@electric-sql/pglite@0.4.1) @@ -1650,7 +3006,7 @@ snapshots: proper-lockfile: 4.1.2 remeda: 2.33.4 std-env: 3.10.0 - valibot: 1.2.0 + valibot: 1.2.0(typescript@5.9.3) zeptomatch: 2.1.0 transitivePeerDependencies: - typescript @@ -1753,6 +3109,23 @@ snapshots: "@standard-schema/spec@1.1.0": {} + "@tokenizer/inflate@0.4.1": + dependencies: + debug: 4.4.3 + token-types: 6.1.2 + transitivePeerDependencies: + - supports-color + + "@tokenizer/token@0.3.0": {} + + "@tsconfig/node10@1.0.12": {} + + "@tsconfig/node12@1.0.11": {} + + "@tsconfig/node14@1.0.3": {} + + "@tsconfig/node16@1.0.4": {} + "@turbo/darwin-64@2.9.3": optional: true @@ -1777,6 +3150,13 @@ snapshots: "@types/json-schema@7.0.15": {} + "@types/jsonwebtoken@9.0.10": + dependencies: + "@types/ms": 2.1.0 + "@types/node": 25.5.2 + + "@types/ms@2.1.0": {} + "@types/node@25.5.2": dependencies: undici-types: 7.18.2 @@ -1785,10 +3165,25 @@ snapshots: dependencies: csstype: 3.2.3 + "@types/strip-bom@3.0.0": {} + + "@types/strip-json-comments@0.0.30": {} + + "@types/validator@13.15.10": {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 + acorn-walk@8.3.5: + dependencies: + acorn: 8.16.0 + acorn@8.16.0: {} ajv@6.14.0: @@ -1813,18 +3208,64 @@ snapshots: ansi-styles@6.2.3: {} + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + append-field@1.0.0: {} + + arg@4.1.3: {} + aws-ssl-profiles@1.1.2: {} + balanced-match@1.0.2: {} + balanced-match@4.0.4: {} better-result@2.7.0: dependencies: "@clack/prompts": 0.11.0 + binary-extensions@2.3.0: {} + + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + on-finished: 2.4.1 + qs: 6.15.0 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + brace-expansion@1.1.13: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@5.0.5: dependencies: balanced-match: 4.0.4 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + buffer-equal-constant-time@1.0.1: {} + + buffer-from@1.1.2: {} + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + + bytes@3.1.2: {} + c12@3.1.0: dependencies: chokidar: 4.0.3 @@ -1840,10 +3281,32 @@ snapshots: pkg-types: 2.3.0 rc9: 2.1.2 + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + chart.js@4.5.1: dependencies: "@kurkle/color": 0.3.4 + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -1854,6 +3317,14 @@ snapshots: citty@0.2.2: {} + class-transformer@0.5.1: {} + + class-validator@0.15.1: + dependencies: + "@types/validator": 13.15.10 + libphonenumber-js: 1.12.41 + validator: 13.15.35 + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -1867,10 +3338,34 @@ snapshots: commander@14.0.3: {} + concat-map@0.0.1: {} + + concat-stream@2.0.0: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + typedarray: 0.0.6 + confbox@0.2.4: {} consola@3.4.2: {} + content-disposition@1.0.1: {} + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + create-require@1.1.1: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -1891,10 +3386,36 @@ snapshots: denque@2.1.0: {} + depd@2.0.0: {} + destr@2.0.5: {} + diff@4.0.4: {} + + dotenv-expand@12.0.3: + dependencies: + dotenv: 16.6.1 + dotenv@16.6.1: {} + dotenv@17.2.3: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + dynamic-dedupe@0.3.0: + dependencies: + xtend: 4.0.2 + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + ee-first@1.1.1: {} + effect@3.20.0: dependencies: "@standard-schema/spec": 1.1.0 @@ -1904,10 +3425,22 @@ snapshots: empathic@2.0.0: {} + encodeurl@2.0.0: {} + env-paths@3.0.0: {} environment@1.1.0: {} + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + escape-html@1.0.3: {} + escape-string-regexp@4.0.0: {} eslint-scope@9.1.2: @@ -1976,8 +3509,43 @@ snapshots: esutils@2.0.3: {} + etag@1.8.1: {} + eventemitter3@5.0.4: {} + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.0.1 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + exsolve@1.0.8: {} fast-check@3.23.2: @@ -1990,12 +3558,38 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-safe-stringify@2.1.1: {} + fast-uri@3.1.0: {} file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 + file-type@21.3.4: + dependencies: + "@tokenizer/inflate": 0.4.1 + strtok3: 10.3.5 + token-types: 6.1.2 + uint8array-extras: 1.5.0 + transitivePeerDependencies: + - supports-color + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -2013,14 +3607,43 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + generate-function@2.3.1: dependencies: is-property: 1.0.2 get-east-asian-width@1.5.0: {} + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.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 + get-port-please@3.2.0: {} + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + giget@2.0.0: dependencies: citty: 0.1.6 @@ -2030,20 +3653,49 @@ snapshots: nypm: 0.6.5 pathe: 2.0.3 + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + globals@17.4.0: {} + gopd@1.2.0: {} + graceful-fs@4.2.11: {} grammex@3.1.12: {} graphmatch@1.1.1: {} + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + hono@4.12.10: {} + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + http-status-codes@2.3.0: {} husky@9.1.7: {} @@ -2052,10 +3704,29 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + ignore@5.3.2: {} imurmurhash@0.1.4: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + is-extglob@2.1.1: {} is-fullwidth-code-point@5.1.0: @@ -2066,10 +3737,16 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-number@7.0.0: {} + + is-promise@4.0.0: {} + is-property@1.0.2: {} isexe@2.0.0: {} + iterare@1.2.1: {} + jiti@2.6.1: {} json-buffer@3.0.1: {} @@ -2080,6 +3757,30 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + jsonwebtoken@9.0.3: + dependencies: + jws: 4.0.1 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.4 + + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -2089,6 +3790,8 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + libphonenumber-js@1.12.41: {} + lint-staged@16.4.0: dependencies: commander: 14.0.3 @@ -2107,10 +3810,28 @@ snapshots: rfdc: 1.4.1 wrap-ansi: 9.0.2 + load-esm@1.0.3: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.once@4.1.1: {} + + lodash@4.17.23: {} + log-update@6.1.0: dependencies: ansi-escapes: 7.3.0 @@ -2123,14 +3844,51 @@ snapshots: lru.min@1.1.4: {} + make-error@1.3.6: {} + + math-intrinsics@1.1.0: {} + + media-typer@0.3.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + mimic-function@5.0.1: {} minimatch@10.2.5: dependencies: brace-expansion: 5.0.5 + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.13 + + minimist@1.2.8: {} + + mkdirp@1.0.4: {} + ms@2.1.3: {} + multer@2.1.1: + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 2.0.0 + type-is: 1.6.18 + mysql2@3.15.3: dependencies: aws-ssl-profiles: 1.1.2 @@ -2149,16 +3907,32 @@ snapshots: natural-compare@1.4.0: {} + negotiator@1.0.0: {} + node-fetch-native@1.6.7: {} + normalize-path@3.0.0: {} + nypm@0.6.5: dependencies: citty: 0.2.2 pathe: 2.0.3 tinyexec: 1.0.4 + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + ohash@2.0.11: {} + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + onetime@7.0.0: dependencies: mimic-function: 5.0.1 @@ -2180,16 +3954,26 @@ snapshots: dependencies: p-limit: 3.1.0 + parseurl@1.3.3: {} + path-exists@4.0.0: {} + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} + path-parse@1.0.7: {} + + path-to-regexp@8.4.2: {} + pathe@2.0.3: {} perfect-debounce@1.0.0: {} picocolors@1.1.1: {} + picomatch@2.3.2: {} + picomatch@4.0.4: {} pkg-types@2.3.0: @@ -2204,14 +3988,16 @@ snapshots: prettier@3.8.1: {} - prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3): dependencies: "@prisma/config": 7.6.0 - "@prisma/dev": 0.24.3 + "@prisma/dev": 0.24.3(typescript@5.9.3) "@prisma/engines": 7.6.0 "@prisma/studio-core": 0.27.3(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) mysql2: 3.15.3 postgres: 3.4.7 + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - "@types/react" - "@types/react-dom" @@ -2225,10 +4011,28 @@ snapshots: retry: 0.12.0 signal-exit: 3.0.7 + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + punycode@2.3.1: {} pure-rand@6.1.0: {} + qs@6.15.0: + dependencies: + side-channel: 1.1.0 + + range-parser@1.2.1: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + rc9@2.1.2: dependencies: defu: 6.1.6 @@ -2241,12 +4045,30 @@ snapshots: react@19.2.4: {} + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + readdirp@4.1.2: {} + reflect-metadata@0.2.2: {} + remeda@2.33.4: {} require-from-string@2.0.2: {} + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + restore-cursor@5.1.0: dependencies: onetime: 7.0.0 @@ -2256,18 +4078,95 @@ snapshots: rfdc@1.4.1: {} + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.2.1: {} + safer-buffer@2.1.2: {} scheduler@0.27.0: {} + semver@7.7.4: {} + + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + seq-queue@0.0.5: {} + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -2284,10 +4183,21 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + sqlstring@2.3.3: {} + statuses@2.0.2: {} + std-env@3.10.0: {} + streamsearch@1.1.0: {} + string-argv@0.3.2: {} string-width@7.2.0: @@ -2301,12 +4211,85 @@ snapshots: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 + strip-bom@3.0.0: {} + + strip-json-comments@2.0.1: {} + + strtok3@10.3.5: + dependencies: + "@tokenizer/token": 0.3.0 + + supports-preserve-symlinks-flag@1.0.0: {} + tinyexec@1.0.4: {} + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + token-types@6.1.2: + dependencies: + "@borewit/text-codec": 0.2.2 + "@tokenizer/token": 0.3.0 + ieee754: 1.2.1 + + tree-kill@1.2.2: {} + + ts-node-dev@2.0.0(@types/node@25.5.2)(typescript@5.9.3): + dependencies: + chokidar: 3.6.0 + dynamic-dedupe: 0.3.0 + minimist: 1.2.8 + mkdirp: 1.0.4 + resolve: 1.22.11 + rimraf: 2.7.1 + source-map-support: 0.5.21 + tree-kill: 1.2.2 + ts-node: 10.9.2(@types/node@25.5.2)(typescript@5.9.3) + tsconfig: 7.0.0 + typescript: 5.9.3 + transitivePeerDependencies: + - "@swc/core" + - "@swc/wasm" + - "@types/node" + + ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3): + dependencies: + "@cspotcode/source-map-support": 0.8.1 + "@tsconfig/node10": 1.0.12 + "@tsconfig/node12": 1.0.11 + "@tsconfig/node14": 1.0.3 + "@tsconfig/node16": 1.0.4 + "@types/node": 25.5.2 + acorn: 8.16.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.9.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + tsconfig@7.0.0: + dependencies: + "@types/strip-bom": 3.0.0 + "@types/strip-json-comments": 0.0.30 + strip-bom: 3.0.0 + strip-json-comments: 2.0.1 + + tslib@2.8.1: {} + turbo@2.9.3: optionalDependencies: "@turbo/darwin-64": 2.9.3 @@ -2320,13 +4303,46 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + + typedarray@0.0.6: {} + + typescript@5.9.3: {} + + uid@2.0.2: + dependencies: + "@lukeed/csprng": 1.1.0 + + uint8array-extras@1.5.0: {} + undici-types@7.18.2: {} + unpipe@1.0.0: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 - valibot@1.2.0: {} + util-deprecate@1.0.2: {} + + v8-compile-cache-lib@3.0.1: {} + + valibot@1.2.0(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + validator@13.15.35: {} + + vary@1.1.2: {} which@2.0.2: dependencies: @@ -2340,8 +4356,14 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.2.0 + wrappy@1.0.2: {} + + xtend@4.0.2: {} + yaml@2.8.3: {} + yn@3.1.1: {} + yocto-queue@0.1.0: {} zeptomatch@2.1.0: From 074942fab40dadb97da26e2217d4839741f2f7dc Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 21:12:14 +0800 Subject: [PATCH 14/22] feat(api-auth): implement refresh token rotation and revoke --- apps/api/.env.example | 1 + apps/api/src/auth/auth.controller.ts | 24 ++++- apps/api/src/auth/auth.service.ts | 107 ++++++++++++++++----- apps/api/src/auth/dto/refresh-token.dto.ts | 7 ++ 4 files changed, 111 insertions(+), 28 deletions(-) create mode 100644 apps/api/src/auth/dto/refresh-token.dto.ts diff --git a/apps/api/.env.example b/apps/api/.env.example index 6406c52..41b5593 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -1,4 +1,5 @@ DATABASE_URL="postgresql://postgres:postgres@localhost:5432/todolist?schema=public" AUTH_ACCESS_SECRET="dev-access-secret" AUTH_ACCESS_EXPIRES_IN_SECONDS="900" +AUTH_REFRESH_EXPIRES_IN_SECONDS="2592000" AUTH_EMAIL_CODE_TTL_SECONDS="300" diff --git a/apps/api/src/auth/auth.controller.ts b/apps/api/src/auth/auth.controller.ts index 6708a53..55d2329 100644 --- a/apps/api/src/auth/auth.controller.ts +++ b/apps/api/src/auth/auth.controller.ts @@ -1,6 +1,7 @@ import { Body, Controller, Post } from "@nestjs/common"; import { AuthService } from "./auth.service"; import { EmailLoginDto } from "./dto/email-login.dto"; +import { RefreshTokenDto } from "./dto/refresh-token.dto"; import { SendEmailCodeDto } from "./dto/send-email-code.dto"; @Controller("auth") @@ -15,14 +16,31 @@ export class AuthController { } @Post("email/login") - async loginWithEmailCode( - @Body() body: EmailLoginDto - ): Promise<{ + async loginWithEmailCode(@Body() body: EmailLoginDto): Promise<{ accessToken: string; tokenType: "Bearer"; expiresInSeconds: number; + refreshToken: string; + refreshExpiresInSeconds: number; user: { id: string; email: string }; }> { return this.authService.loginWithEmailCode(body.email, body.code); } + + @Post("token/refresh") + async refreshTokens(@Body() body: RefreshTokenDto): Promise<{ + accessToken: string; + tokenType: "Bearer"; + expiresInSeconds: number; + refreshToken: string; + refreshExpiresInSeconds: number; + user: { id: string; email: string }; + }> { + return this.authService.refreshTokens(body.refreshToken); + } + + @Post("token/revoke") + async revokeRefreshToken(@Body() body: RefreshTokenDto): Promise<{ success: boolean }> { + return this.authService.revokeRefreshToken(body.refreshToken); + } } diff --git a/apps/api/src/auth/auth.service.ts b/apps/api/src/auth/auth.service.ts index 9ede99c..d48f917 100644 --- a/apps/api/src/auth/auth.service.ts +++ b/apps/api/src/auth/auth.service.ts @@ -13,10 +13,27 @@ type AuthUser = { email: string; }; +type RefreshTokenEntry = { + userId: string; + expiresAt: number; + revokedAt?: number; +}; + +type AuthTokenResult = { + accessToken: string; + tokenType: "Bearer"; + expiresInSeconds: number; + refreshToken: string; + refreshExpiresInSeconds: number; + user: AuthUser; +}; + @Injectable() export class AuthService { private readonly emailCodeStore = new Map(); - private readonly userStore = new Map(); + private readonly userStoreByEmail = new Map(); + private readonly userStoreById = new Map(); + private readonly refreshTokenStore = new Map(); constructor( private readonly configService: ConfigService, @@ -39,15 +56,7 @@ export class AuthService { }; } - async loginWithEmailCode( - email: string, - code: string - ): Promise<{ - accessToken: string; - tokenType: "Bearer"; - expiresInSeconds: number; - user: AuthUser; - }> { + async loginWithEmailCode(email: string, code: string): Promise { const lowerEmail = email.toLowerCase(); const codeEntry = this.emailCodeStore.get(lowerEmail); @@ -67,24 +76,43 @@ export class AuthService { this.emailCodeStore.delete(lowerEmail); const user = this.getOrCreateUser(lowerEmail); - const expiresInSeconds = Number( - this.configService.get("AUTH_ACCESS_EXPIRES_IN_SECONDS") ?? 900 - ); - const accessToken = await this.jwtService.signAsync({ - sub: user.id, - email: user.email - }); + return this.issueTokens(user); + } - return { - accessToken, - tokenType: "Bearer", - expiresInSeconds, - user - }; + async refreshTokens(refreshToken: string): Promise { + const entry = this.refreshTokenStore.get(refreshToken); + if (!entry) { + throw new UnauthorizedException("刷新令牌不存在"); + } + if (entry.revokedAt) { + throw new UnauthorizedException("刷新令牌已注销"); + } + if (entry.expiresAt < Date.now()) { + this.refreshTokenStore.delete(refreshToken); + throw new UnauthorizedException("刷新令牌已过期"); + } + + const user = this.userStoreById.get(entry.userId); + if (!user) { + throw new UnauthorizedException("用户不存在"); + } + + entry.revokedAt = Date.now(); + return this.issueTokens(user); + } + + async revokeRefreshToken(refreshToken: string): Promise<{ success: boolean }> { + const entry = this.refreshTokenStore.get(refreshToken); + if (!entry) { + return { success: true }; + } + + entry.revokedAt = Date.now(); + return { success: true }; } private getOrCreateUser(email: string): AuthUser { - const existingUser = this.userStore.get(email); + const existingUser = this.userStoreByEmail.get(email); if (existingUser) { return existingUser; } @@ -93,7 +121,8 @@ export class AuthService { id: randomUUID(), email }; - this.userStore.set(email, newUser); + this.userStoreByEmail.set(email, newUser); + this.userStoreById.set(newUser.id, newUser); return newUser; } @@ -101,4 +130,32 @@ export class AuthService { private generateCode(): string { return String(Math.floor(100000 + Math.random() * 900000)); } + + private async issueTokens(user: AuthUser): Promise { + const accessExpiresInSeconds = Number( + this.configService.get("AUTH_ACCESS_EXPIRES_IN_SECONDS") ?? 900 + ); + const refreshExpiresInSeconds = Number( + this.configService.get("AUTH_REFRESH_EXPIRES_IN_SECONDS") ?? 2592000 + ); + const accessToken = await this.jwtService.signAsync({ + sub: user.id, + email: user.email + }); + const refreshToken = `${randomUUID()}${randomUUID()}`; + + this.refreshTokenStore.set(refreshToken, { + userId: user.id, + expiresAt: Date.now() + refreshExpiresInSeconds * 1000 + }); + + return { + accessToken, + tokenType: "Bearer", + expiresInSeconds: accessExpiresInSeconds, + refreshToken, + refreshExpiresInSeconds, + user + }; + } } diff --git a/apps/api/src/auth/dto/refresh-token.dto.ts b/apps/api/src/auth/dto/refresh-token.dto.ts new file mode 100644 index 0000000..2d91b3c --- /dev/null +++ b/apps/api/src/auth/dto/refresh-token.dto.ts @@ -0,0 +1,7 @@ +import { IsString, MinLength } from "class-validator"; + +export class RefreshTokenDto { + @IsString() + @MinLength(20) + refreshToken!: string; +} From 485fe43140013054b348d0858a74432c919ab6a6 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 21:15:32 +0800 Subject: [PATCH 15/22] feat(api-auth): add oauth strategies for github qq wechat --- apps/api/.env.example | 13 ++ apps/api/package.json | 2 + apps/api/src/auth/auth.controller.ts | 57 ++++++++- apps/api/src/auth/auth.module.ts | 7 +- .../src/auth/strategies/github.strategy.ts | 32 +++++ apps/api/src/auth/strategies/qq.strategy.ts | 33 +++++ .../src/auth/strategies/wechat.strategy.ts | 36 ++++++ pnpm-lock.yaml | 116 ++++++++++++++++++ 8 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 apps/api/src/auth/strategies/github.strategy.ts create mode 100644 apps/api/src/auth/strategies/qq.strategy.ts create mode 100644 apps/api/src/auth/strategies/wechat.strategy.ts diff --git a/apps/api/.env.example b/apps/api/.env.example index 41b5593..69712b2 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -3,3 +3,16 @@ AUTH_ACCESS_SECRET="dev-access-secret" AUTH_ACCESS_EXPIRES_IN_SECONDS="900" AUTH_REFRESH_EXPIRES_IN_SECONDS="2592000" AUTH_EMAIL_CODE_TTL_SECONDS="300" +OAUTH_GITHUB_CLIENT_ID="github-client-id" +OAUTH_GITHUB_CLIENT_SECRET="github-client-secret" +OAUTH_GITHUB_CALLBACK_URL="http://localhost:3000/auth/oauth/github/callback" +OAUTH_QQ_CLIENT_ID="qq-client-id" +OAUTH_QQ_CLIENT_SECRET="qq-client-secret" +OAUTH_QQ_CALLBACK_URL="http://localhost:3000/auth/oauth/qq/callback" +OAUTH_QQ_AUTH_URL="https://graph.qq.com/oauth2.0/authorize" +OAUTH_QQ_TOKEN_URL="https://graph.qq.com/oauth2.0/token" +OAUTH_WECHAT_CLIENT_ID="wechat-client-id" +OAUTH_WECHAT_CLIENT_SECRET="wechat-client-secret" +OAUTH_WECHAT_CALLBACK_URL="http://localhost:3000/auth/oauth/wechat/callback" +OAUTH_WECHAT_AUTH_URL="https://open.weixin.qq.com/connect/qrconnect" +OAUTH_WECHAT_TOKEN_URL="https://api.weixin.qq.com/sns/oauth2/access_token" diff --git a/apps/api/package.json b/apps/api/package.json index e02e42d..a136df6 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -15,6 +15,8 @@ "license": "GPL-3.0-or-later", "devDependencies": { "@types/node": "^25.5.2", + "@types/passport-github2": "^1.2.9", + "@types/passport-oauth2": "^1.8.0", "dotenv": "^16.6.1", "prisma": "^7.6.0", "ts-node": "^10.9.2", diff --git a/apps/api/src/auth/auth.controller.ts b/apps/api/src/auth/auth.controller.ts index 55d2329..ecf7bd3 100644 --- a/apps/api/src/auth/auth.controller.ts +++ b/apps/api/src/auth/auth.controller.ts @@ -1,4 +1,5 @@ -import { Body, Controller, Post } from "@nestjs/common"; +import { Body, Controller, Get, Post, Req, UseGuards } from "@nestjs/common"; +import { AuthGuard } from "@nestjs/passport"; import { AuthService } from "./auth.service"; import { EmailLoginDto } from "./dto/email-login.dto"; import { RefreshTokenDto } from "./dto/refresh-token.dto"; @@ -43,4 +44,58 @@ export class AuthController { async revokeRefreshToken(@Body() body: RefreshTokenDto): Promise<{ success: boolean }> { return this.authService.revokeRefreshToken(body.refreshToken); } + + @Get("oauth/github") + @UseGuards(AuthGuard("github")) + githubLogin(): void {} + + @Get("oauth/github/callback") + @UseGuards(AuthGuard("github")) + githubCallback(@Req() req: { user: unknown }): { + success: boolean; + provider: "github"; + profile: unknown; + } { + return { + success: true, + provider: "github", + profile: req.user + }; + } + + @Get("oauth/qq") + @UseGuards(AuthGuard("qq")) + qqLogin(): void {} + + @Get("oauth/qq/callback") + @UseGuards(AuthGuard("qq")) + qqCallback(@Req() req: { user: unknown }): { + success: boolean; + provider: "qq"; + profile: unknown; + } { + return { + success: true, + provider: "qq", + profile: req.user + }; + } + + @Get("oauth/wechat") + @UseGuards(AuthGuard("wechat")) + wechatLogin(): void {} + + @Get("oauth/wechat/callback") + @UseGuards(AuthGuard("wechat")) + wechatCallback(@Req() req: { user: unknown }): { + success: boolean; + provider: "wechat"; + profile: unknown; + } { + return { + success: true, + provider: "wechat", + profile: req.user + }; + } } diff --git a/apps/api/src/auth/auth.module.ts b/apps/api/src/auth/auth.module.ts index f8b73ce..e108ac3 100644 --- a/apps/api/src/auth/auth.module.ts +++ b/apps/api/src/auth/auth.module.ts @@ -1,12 +1,17 @@ import { Module } from "@nestjs/common"; import { ConfigModule, ConfigService } from "@nestjs/config"; import { JwtModule } from "@nestjs/jwt"; +import { PassportModule } from "@nestjs/passport"; import { AuthController } from "./auth.controller"; import { AuthService } from "./auth.service"; +import { GithubStrategy } from "./strategies/github.strategy"; +import { QqStrategy } from "./strategies/qq.strategy"; +import { WechatStrategy } from "./strategies/wechat.strategy"; @Module({ imports: [ ConfigModule, + PassportModule.register({ session: false }), JwtModule.registerAsync({ inject: [ConfigService], useFactory: (configService: ConfigService) => { @@ -22,6 +27,6 @@ import { AuthService } from "./auth.service"; }) ], controllers: [AuthController], - providers: [AuthService] + providers: [AuthService, GithubStrategy, QqStrategy, WechatStrategy] }) export class AuthModule {} diff --git a/apps/api/src/auth/strategies/github.strategy.ts b/apps/api/src/auth/strategies/github.strategy.ts new file mode 100644 index 0000000..3b3133d --- /dev/null +++ b/apps/api/src/auth/strategies/github.strategy.ts @@ -0,0 +1,32 @@ +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { PassportStrategy } from "@nestjs/passport"; +import { Profile, Strategy } from "passport-github2"; + +@Injectable() +export class GithubStrategy extends PassportStrategy(Strategy, "github") { + constructor(configService: ConfigService) { + super({ + clientID: configService.get("OAUTH_GITHUB_CLIENT_ID") ?? "github-client-id", + clientSecret: + configService.get("OAUTH_GITHUB_CLIENT_SECRET") ?? "github-client-secret", + callbackURL: + configService.get("OAUTH_GITHUB_CALLBACK_URL") ?? + "http://localhost:3000/auth/oauth/github/callback", + scope: ["user:email"] + }); + } + + async validate( + accessToken: string, + refreshToken: string, + profile: Profile + ): Promise<{ provider: "github"; accessToken: string; refreshToken: string; profile: Profile }> { + return { + provider: "github", + accessToken, + refreshToken, + profile + }; + } +} diff --git a/apps/api/src/auth/strategies/qq.strategy.ts b/apps/api/src/auth/strategies/qq.strategy.ts new file mode 100644 index 0000000..5191151 --- /dev/null +++ b/apps/api/src/auth/strategies/qq.strategy.ts @@ -0,0 +1,33 @@ +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { PassportStrategy } from "@nestjs/passport"; +import { Strategy } from "passport-oauth2"; + +@Injectable() +export class QqStrategy extends PassportStrategy(Strategy, "qq") { + constructor(configService: ConfigService) { + super({ + authorizationURL: + configService.get("OAUTH_QQ_AUTH_URL") ?? "https://graph.qq.com/oauth2.0/authorize", + tokenURL: + configService.get("OAUTH_QQ_TOKEN_URL") ?? "https://graph.qq.com/oauth2.0/token", + clientID: configService.get("OAUTH_QQ_CLIENT_ID") ?? "qq-client-id", + clientSecret: configService.get("OAUTH_QQ_CLIENT_SECRET") ?? "qq-client-secret", + callbackURL: + configService.get("OAUTH_QQ_CALLBACK_URL") ?? + "http://localhost:3000/auth/oauth/qq/callback", + scope: ["get_user_info"] + }); + } + + async validate( + accessToken: string, + refreshToken: string + ): Promise<{ provider: "qq"; accessToken: string; refreshToken: string }> { + return { + provider: "qq", + accessToken, + refreshToken + }; + } +} diff --git a/apps/api/src/auth/strategies/wechat.strategy.ts b/apps/api/src/auth/strategies/wechat.strategy.ts new file mode 100644 index 0000000..1e4343b --- /dev/null +++ b/apps/api/src/auth/strategies/wechat.strategy.ts @@ -0,0 +1,36 @@ +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { PassportStrategy } from "@nestjs/passport"; +import { Strategy } from "passport-oauth2"; + +@Injectable() +export class WechatStrategy extends PassportStrategy(Strategy, "wechat") { + constructor(configService: ConfigService) { + super({ + authorizationURL: + configService.get("OAUTH_WECHAT_AUTH_URL") ?? + "https://open.weixin.qq.com/connect/qrconnect", + tokenURL: + configService.get("OAUTH_WECHAT_TOKEN_URL") ?? + "https://api.weixin.qq.com/sns/oauth2/access_token", + clientID: configService.get("OAUTH_WECHAT_CLIENT_ID") ?? "wechat-client-id", + clientSecret: + configService.get("OAUTH_WECHAT_CLIENT_SECRET") ?? "wechat-client-secret", + callbackURL: + configService.get("OAUTH_WECHAT_CALLBACK_URL") ?? + "http://localhost:3000/auth/oauth/wechat/callback", + scope: ["snsapi_login"] + }); + } + + async validate( + accessToken: string, + refreshToken: string + ): Promise<{ provider: "wechat"; accessToken: string; refreshToken: string }> { + return { + provider: "wechat", + accessToken, + refreshToken + }; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6c8607a..a4d24c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,6 +43,9 @@ importers: "@nestjs/jwt": specifier: ^11.0.2 version: 11.0.2(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)) + "@nestjs/passport": + specifier: ^11.0.5 + version: 11.0.5(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) "@nestjs/platform-express": specifier: ^11.1.18 version: 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18) @@ -55,6 +58,15 @@ importers: class-validator: specifier: ^0.15.1 version: 0.15.1 + passport: + specifier: ^0.7.0 + version: 0.7.0 + passport-github2: + specifier: ^0.1.12 + version: 0.1.12 + passport-oauth2: + specifier: ^1.8.0 + version: 1.8.0 reflect-metadata: specifier: ^0.2.2 version: 0.2.2 @@ -320,6 +332,15 @@ packages: peerDependencies: "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + "@nestjs/passport@11.0.5": + resolution: + { + integrity: sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ== + } + peerDependencies: + "@nestjs/common": ^10.0.0 || ^11.0.0 + passport: ^0.5.0 || ^0.6.0 || ^0.7.0 + "@nestjs/platform-express@11.1.18": resolution: { @@ -787,6 +808,13 @@ packages: } engines: { node: 18 || 20 || >=22 } + base64url@3.0.1: + resolution: + { + integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + } + engines: { node: ">=6.0.0" } + better-result@2.7.0: resolution: { @@ -2002,6 +2030,12 @@ packages: engines: { node: ">=18" } hasBin: true + oauth@0.10.2: + resolution: + { + integrity: sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q== + } + object-assign@4.1.1: resolution: { @@ -2070,6 +2104,34 @@ packages: } engines: { node: ">= 0.8" } + passport-github2@0.1.12: + resolution: + { + integrity: sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw== + } + engines: { node: ">= 0.8.0" } + + passport-oauth2@1.8.0: + resolution: + { + integrity: sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA== + } + engines: { node: ">= 0.4.0" } + + passport-strategy@1.0.0: + resolution: + { + integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA== + } + engines: { node: ">= 0.4.0" } + + passport@0.7.0: + resolution: + { + integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ== + } + engines: { node: ">= 0.4.0" } + path-exists@4.0.0: resolution: { @@ -2109,6 +2171,12 @@ packages: integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== } + pause@0.0.1: + resolution: + { + integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg== + } + perfect-debounce@1.0.0: resolution: { @@ -2685,6 +2753,12 @@ packages: engines: { node: ">=14.17" } hasBin: true + uid2@0.0.4: + resolution: + { + integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA== + } + uid@2.0.2: resolution: { @@ -2724,6 +2798,13 @@ packages: integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== } + utils-merge@1.0.1: + resolution: + { + integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + } + engines: { node: ">= 0.4.0" } + v8-compile-cache-lib@3.0.1: resolution: { @@ -2951,6 +3032,11 @@ snapshots: "@types/jsonwebtoken": 9.0.10 jsonwebtoken: 9.0.3 + "@nestjs/passport@11.0.5(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)": + dependencies: + "@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + passport: 0.7.0 + "@nestjs/platform-express@11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)": dependencies: "@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -3223,6 +3309,8 @@ snapshots: balanced-match@4.0.4: {} + base64url@3.0.1: {} + better-result@2.7.0: dependencies: "@clack/prompts": 0.11.0 @@ -3919,6 +4007,8 @@ snapshots: pathe: 2.0.3 tinyexec: 1.0.4 + oauth@0.10.2: {} + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -3956,6 +4046,26 @@ snapshots: parseurl@1.3.3: {} + passport-github2@0.1.12: + dependencies: + passport-oauth2: 1.8.0 + + passport-oauth2@1.8.0: + dependencies: + base64url: 3.0.1 + oauth: 0.10.2 + passport-strategy: 1.0.0 + uid2: 0.0.4 + utils-merge: 1.0.1 + + passport-strategy@1.0.0: {} + + passport@0.7.0: + dependencies: + passport-strategy: 1.0.0 + pause: 0.0.1 + utils-merge: 1.0.1 + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -3968,6 +4078,8 @@ snapshots: pathe@2.0.3: {} + pause@0.0.1: {} + perfect-debounce@1.0.0: {} picocolors@1.1.1: {} @@ -4318,6 +4430,8 @@ snapshots: typescript@5.9.3: {} + uid2@0.0.4: {} + uid@2.0.2: dependencies: "@lukeed/csprng": 1.1.0 @@ -4334,6 +4448,8 @@ snapshots: util-deprecate@1.0.2: {} + utils-merge@1.0.1: {} + v8-compile-cache-lib@3.0.1: {} valibot@1.2.0(typescript@5.9.3): From 62b0514da787761be8e43b653f5877dd65509237 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sat, 4 Apr 2026 21:21:43 +0800 Subject: [PATCH 16/22] feat(api-auth): add totp 2fa enrollment and verify --- apps/api/.env.example | 1 + apps/api/package.json | 2 + apps/api/src/auth/auth.controller.ts | 19 + apps/api/src/auth/auth.service.ts | 50 +++ .../api/src/auth/dto/two-factor-enroll.dto.ts | 6 + .../api/src/auth/dto/two-factor-verify.dto.ts | 11 + pnpm-lock.yaml | 411 +++++++++++++----- 7 files changed, 384 insertions(+), 116 deletions(-) create mode 100644 apps/api/src/auth/dto/two-factor-enroll.dto.ts create mode 100644 apps/api/src/auth/dto/two-factor-verify.dto.ts diff --git a/apps/api/.env.example b/apps/api/.env.example index 69712b2..4394718 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -3,6 +3,7 @@ AUTH_ACCESS_SECRET="dev-access-secret" AUTH_ACCESS_EXPIRES_IN_SECONDS="900" AUTH_REFRESH_EXPIRES_IN_SECONDS="2592000" AUTH_EMAIL_CODE_TTL_SECONDS="300" +AUTH_TOTP_ISSUER="TodoList" OAUTH_GITHUB_CLIENT_ID="github-client-id" OAUTH_GITHUB_CLIENT_SECRET="github-client-secret" OAUTH_GITHUB_CALLBACK_URL="http://localhost:3000/auth/oauth/github/callback" diff --git a/apps/api/package.json b/apps/api/package.json index a136df6..a1327aa 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -30,9 +30,11 @@ "@nestjs/core": "^11.1.18", "@nestjs/jwt": "^11.0.2", "@nestjs/platform-express": "^11.1.18", + "@otplib/preset-default": "^12.0.1", "@prisma/client": "^7.6.0", "class-transformer": "^0.5.1", "class-validator": "^0.15.1", + "otplib": "^13.4.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.2" } diff --git a/apps/api/src/auth/auth.controller.ts b/apps/api/src/auth/auth.controller.ts index ecf7bd3..1641223 100644 --- a/apps/api/src/auth/auth.controller.ts +++ b/apps/api/src/auth/auth.controller.ts @@ -4,6 +4,8 @@ import { AuthService } from "./auth.service"; import { EmailLoginDto } from "./dto/email-login.dto"; import { RefreshTokenDto } from "./dto/refresh-token.dto"; import { SendEmailCodeDto } from "./dto/send-email-code.dto"; +import { TwoFactorEnrollDto } from "./dto/two-factor-enroll.dto"; +import { TwoFactorVerifyDto } from "./dto/two-factor-verify.dto"; @Controller("auth") export class AuthController { @@ -45,6 +47,23 @@ export class AuthController { return this.authService.revokeRefreshToken(body.refreshToken); } + @Post("2fa/enroll") + async enrollTwoFactor(@Body() body: TwoFactorEnrollDto): Promise<{ + userId: string; + secret: string; + otpauthUrl: string; + enabled: boolean; + }> { + return this.authService.enrollTwoFactor(body.email); + } + + @Post("2fa/verify") + async verifyTwoFactor( + @Body() body: TwoFactorVerifyDto + ): Promise<{ success: boolean; enabled: boolean }> { + return this.authService.verifyTwoFactor(body.email, body.token); + } + @Get("oauth/github") @UseGuards(AuthGuard("github")) githubLogin(): void {} diff --git a/apps/api/src/auth/auth.service.ts b/apps/api/src/auth/auth.service.ts index d48f917..4e25fa1 100644 --- a/apps/api/src/auth/auth.service.ts +++ b/apps/api/src/auth/auth.service.ts @@ -2,6 +2,7 @@ import { Injectable, UnauthorizedException } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { JwtService } from "@nestjs/jwt"; import { randomUUID } from "node:crypto"; +import { authenticator } from "@otplib/preset-default"; type EmailCodeEntry = { code: string; @@ -19,6 +20,11 @@ type RefreshTokenEntry = { revokedAt?: number; }; +type TwoFactorEntry = { + secret: string; + enabled: boolean; +}; + type AuthTokenResult = { accessToken: string; tokenType: "Bearer"; @@ -34,6 +40,7 @@ export class AuthService { private readonly userStoreByEmail = new Map(); private readonly userStoreById = new Map(); private readonly refreshTokenStore = new Map(); + private readonly twoFactorStore = new Map(); constructor( private readonly configService: ConfigService, @@ -111,6 +118,49 @@ export class AuthService { return { success: true }; } + async enrollTwoFactor( + email: string + ): Promise<{ userId: string; secret: string; otpauthUrl: string; enabled: boolean }> { + const user = this.getOrCreateUser(email.toLowerCase()); + const secret = authenticator.generateSecret(); + const issuer = this.configService.get("AUTH_TOTP_ISSUER") ?? "TodoList"; + const otpauthUrl = authenticator.keyuri(user.email, issuer, secret); + + this.twoFactorStore.set(user.id, { + secret, + enabled: false + }); + + return { + userId: user.id, + secret, + otpauthUrl, + enabled: false + }; + } + + async verifyTwoFactor( + email: string, + token: string + ): Promise<{ success: boolean; enabled: boolean }> { + const user = this.getOrCreateUser(email.toLowerCase()); + const entry = this.twoFactorStore.get(user.id); + if (!entry) { + throw new UnauthorizedException("尚未启用两步验证"); + } + + const valid = authenticator.check(token, entry.secret); + if (!valid) { + throw new UnauthorizedException("两步验证码错误"); + } + + entry.enabled = true; + return { + success: true, + enabled: true + }; + } + private getOrCreateUser(email: string): AuthUser { const existingUser = this.userStoreByEmail.get(email); if (existingUser) { diff --git a/apps/api/src/auth/dto/two-factor-enroll.dto.ts b/apps/api/src/auth/dto/two-factor-enroll.dto.ts new file mode 100644 index 0000000..2dea4ef --- /dev/null +++ b/apps/api/src/auth/dto/two-factor-enroll.dto.ts @@ -0,0 +1,6 @@ +import { IsEmail } from "class-validator"; + +export class TwoFactorEnrollDto { + @IsEmail() + email!: string; +} diff --git a/apps/api/src/auth/dto/two-factor-verify.dto.ts b/apps/api/src/auth/dto/two-factor-verify.dto.ts new file mode 100644 index 0000000..f9317bd --- /dev/null +++ b/apps/api/src/auth/dto/two-factor-verify.dto.ts @@ -0,0 +1,11 @@ +import { IsEmail, IsString, Length, Matches } from "class-validator"; + +export class TwoFactorVerifyDto { + @IsEmail() + email!: string; + + @IsString() + @Length(6, 6) + @Matches(/^\d{6}$/) + token!: string; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4d24c9..051c827 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,12 +43,12 @@ importers: "@nestjs/jwt": specifier: ^11.0.2 version: 11.0.2(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)) - "@nestjs/passport": - specifier: ^11.0.5 - version: 11.0.5(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) "@nestjs/platform-express": specifier: ^11.1.18 version: 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18) + "@otplib/preset-default": + specifier: ^12.0.1 + version: 12.0.1 "@prisma/client": specifier: ^7.6.0 version: 7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) @@ -58,15 +58,9 @@ importers: class-validator: specifier: ^0.15.1 version: 0.15.1 - passport: - specifier: ^0.7.0 - version: 0.7.0 - passport-github2: - specifier: ^0.1.12 - version: 0.1.12 - passport-oauth2: - specifier: ^1.8.0 - version: 1.8.0 + otplib: + specifier: ^13.4.0 + version: 13.4.0 reflect-metadata: specifier: ^0.2.2 version: 0.2.2 @@ -77,6 +71,12 @@ importers: "@types/node": specifier: ^25.5.2 version: 25.5.2 + "@types/passport-github2": + specifier: ^1.2.9 + version: 1.2.9 + "@types/passport-oauth2": + specifier: ^1.8.0 + version: 1.8.0 dotenv: specifier: ^16.6.1 version: 16.6.1 @@ -332,15 +332,6 @@ packages: peerDependencies: "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - "@nestjs/passport@11.0.5": - resolution: - { - integrity: sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ== - } - peerDependencies: - "@nestjs/common": ^10.0.0 || ^11.0.0 - passport: ^0.5.0 || ^0.6.0 || ^0.7.0 - "@nestjs/platform-express@11.1.18": resolution: { @@ -350,6 +341,13 @@ packages: "@nestjs/common": ^11.0.0 "@nestjs/core": ^11.0.0 + "@noble/hashes@2.0.1": + resolution: + { + integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw== + } + engines: { node: ">= 20.19.0" } + "@nuxt/opencollective@0.4.1": resolution: { @@ -358,6 +356,69 @@ packages: engines: { node: ^14.18.0 || >=16.10.0, npm: ">=5.10.0" } hasBin: true + "@otplib/core@12.0.1": + resolution: + { + integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA== + } + + "@otplib/core@13.4.0": + resolution: + { + integrity: sha512-JqOGcvZQi2wIkEQo8f3/iAjstavpXy6gouIDMHygjNuH6Q0FjbHOiXMdcE94RwfgDNMABhzwUmvaPsxvgm9NYw== + } + + "@otplib/hotp@13.4.0": + resolution: + { + integrity: sha512-MJjE0x06mn2ptymz5qZmQveb+vWFuaIftqE0b5/TZZqUOK7l97cV8lRTmid5BpAQMwJDNLW6RnYxGeCRiNdekw== + } + + "@otplib/plugin-base32-scure@13.4.0": + resolution: + { + integrity: sha512-/t9YWJmMbB8bF5z8mXrBZc2FXBe8B/3hG5FhWr9K8cFwFhyxScbPysmZe8s1UTzSA6N+s8Uv8aIfCtVXPNjJWw== + } + + "@otplib/plugin-crypto-noble@13.4.0": + resolution: + { + integrity: sha512-KrvE4m7Zv+TT1944HzgqFJWJpKb6AyoxDbvhPStmBqdMlv5Gekb80d66cuFRL08kkPgJ5gXUSb5SFpYeB+bACg== + } + + "@otplib/plugin-crypto@12.0.1": + resolution: + { + integrity: sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g== + } + deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths + + "@otplib/plugin-thirty-two@12.0.1": + resolution: + { + integrity: sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA== + } + deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths + + "@otplib/preset-default@12.0.1": + resolution: + { + integrity: sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ== + } + deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths + + "@otplib/totp@13.4.0": + resolution: + { + integrity: sha512-dK+vl0f0ekzf6mCENRI9AKS2NJUC7OjI3+X8e7QSnhQ2WM7I+i4PGpb3QxKi5hxjTtwVuoZwXR2CFtXdcRtNdQ== + } + + "@otplib/uri@13.4.0": + resolution: + { + integrity: sha512-x1ozBa5bPbdZCrrTL/HK21qchiK7jYElTu+0ft22abeEhiLYgH1+SIULvOcVk3CK8YwF4kdcidvkq4ciejucJA== + } + "@prisma/client-runtime-utils@7.6.0": resolution: { @@ -555,6 +616,12 @@ packages: "@types/react": optional: true + "@scure/base@2.0.0": + resolution: + { + integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w== + } + "@standard-schema/spec@1.1.0": resolution: { @@ -646,6 +713,18 @@ packages: cpu: [arm64] os: [win32] + "@types/body-parser@1.19.6": + resolution: + { + integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g== + } + + "@types/connect@3.4.38": + resolution: + { + integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + } + "@types/esrecurse@4.3.1": resolution: { @@ -658,6 +737,24 @@ packages: integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== } + "@types/express-serve-static-core@5.1.1": + resolution: + { + integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A== + } + + "@types/express@5.0.6": + resolution: + { + integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA== + } + + "@types/http-errors@2.0.5": + resolution: + { + integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== + } + "@types/json-schema@7.0.15": resolution: { @@ -682,12 +779,60 @@ packages: integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg== } + "@types/oauth@0.9.6": + resolution: + { + integrity: sha512-H9TRCVKBNOhZZmyHLqFt9drPM9l+ShWiqqJijU1B8P3DX3ub84NjxDuy+Hjrz+fEca5Kwip3qPMKNyiLgNJtIA== + } + + "@types/passport-github2@1.2.9": + resolution: + { + integrity: sha512-/nMfiPK2E6GKttwBzwj0Wjaot8eHrM57hnWxu52o6becr5/kXlH/4yE2v2rh234WGvSgEEzIII02Nc5oC5xEHA== + } + + "@types/passport-oauth2@1.8.0": + resolution: + { + integrity: sha512-6//z+4orIOy/g3zx17HyQ71GSRK4bs7Sb+zFasRoc2xzlv7ZCJ+vkDBYFci8U6HY+or6Zy7ajf4mz4rK7nsWJQ== + } + + "@types/passport@1.0.17": + resolution: + { + integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg== + } + + "@types/qs@6.15.0": + resolution: + { + integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow== + } + + "@types/range-parser@1.2.7": + resolution: + { + integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + } + "@types/react@19.2.14": resolution: { integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w== } + "@types/send@1.2.1": + resolution: + { + integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ== + } + + "@types/serve-static@2.2.0": + resolution: + { + integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ== + } + "@types/strip-bom@3.0.0": resolution: { @@ -808,13 +953,6 @@ packages: } engines: { node: 18 || 20 || >=22 } - base64url@3.0.1: - resolution: - { - integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== - } - engines: { node: ">=6.0.0" } - better-result@2.7.0: resolution: { @@ -2030,12 +2168,6 @@ packages: engines: { node: ">=18" } hasBin: true - oauth@0.10.2: - resolution: - { - integrity: sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q== - } - object-assign@4.1.1: resolution: { @@ -2083,6 +2215,12 @@ packages: } engines: { node: ">= 0.8.0" } + otplib@13.4.0: + resolution: + { + integrity: sha512-RUcYcRMCgRWhUE/XabRppXpUwCwaWBNHe5iPXhdvP8wwDGpGpsIf/kxX/ec3zFsOaM1Oq8lEhUqDwk6W7DHkwg== + } + p-limit@3.1.0: resolution: { @@ -2104,34 +2242,6 @@ packages: } engines: { node: ">= 0.8" } - passport-github2@0.1.12: - resolution: - { - integrity: sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw== - } - engines: { node: ">= 0.8.0" } - - passport-oauth2@1.8.0: - resolution: - { - integrity: sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA== - } - engines: { node: ">= 0.4.0" } - - passport-strategy@1.0.0: - resolution: - { - integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA== - } - engines: { node: ">= 0.4.0" } - - passport@0.7.0: - resolution: - { - integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ== - } - engines: { node: ">= 0.4.0" } - path-exists@4.0.0: resolution: { @@ -2171,12 +2281,6 @@ packages: integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== } - pause@0.0.1: - resolution: - { - integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg== - } - perfect-debounce@1.0.0: resolution: { @@ -2633,6 +2737,13 @@ packages: } engines: { node: ">= 0.4" } + thirty-two@1.0.2: + resolution: + { + integrity: sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA== + } + engines: { node: ">=0.2.6" } + tinyexec@1.0.4: resolution: { @@ -2753,12 +2864,6 @@ packages: engines: { node: ">=14.17" } hasBin: true - uid2@0.0.4: - resolution: - { - integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA== - } - uid@2.0.2: resolution: { @@ -2798,13 +2903,6 @@ packages: integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== } - utils-merge@1.0.1: - resolution: - { - integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - } - engines: { node: ">= 0.4.0" } - v8-compile-cache-lib@3.0.1: resolution: { @@ -3032,11 +3130,6 @@ snapshots: "@types/jsonwebtoken": 9.0.10 jsonwebtoken: 9.0.3 - "@nestjs/passport@11.0.5(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)": - dependencies: - "@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - passport: 0.7.0 - "@nestjs/platform-express@11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)": dependencies: "@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -3049,10 +3142,56 @@ snapshots: transitivePeerDependencies: - supports-color + "@noble/hashes@2.0.1": {} + "@nuxt/opencollective@0.4.1": dependencies: consola: 3.4.2 + "@otplib/core@12.0.1": {} + + "@otplib/core@13.4.0": {} + + "@otplib/hotp@13.4.0": + dependencies: + "@otplib/core": 13.4.0 + "@otplib/uri": 13.4.0 + + "@otplib/plugin-base32-scure@13.4.0": + dependencies: + "@otplib/core": 13.4.0 + "@scure/base": 2.0.0 + + "@otplib/plugin-crypto-noble@13.4.0": + dependencies: + "@noble/hashes": 2.0.1 + "@otplib/core": 13.4.0 + + "@otplib/plugin-crypto@12.0.1": + dependencies: + "@otplib/core": 12.0.1 + + "@otplib/plugin-thirty-two@12.0.1": + dependencies: + "@otplib/core": 12.0.1 + thirty-two: 1.0.2 + + "@otplib/preset-default@12.0.1": + dependencies: + "@otplib/core": 12.0.1 + "@otplib/plugin-crypto": 12.0.1 + "@otplib/plugin-thirty-two": 12.0.1 + + "@otplib/totp@13.4.0": + dependencies: + "@otplib/core": 13.4.0 + "@otplib/hotp": 13.4.0 + "@otplib/uri": 13.4.0 + + "@otplib/uri@13.4.0": + dependencies: + "@otplib/core": 13.4.0 + "@prisma/client-runtime-utils@7.6.0": {} "@prisma/client@7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)": @@ -3193,6 +3332,8 @@ snapshots: optionalDependencies: "@types/react": 19.2.14 + "@scure/base@2.0.0": {} + "@standard-schema/spec@1.1.0": {} "@tokenizer/inflate@0.4.1": @@ -3230,10 +3371,34 @@ snapshots: "@turbo/windows-arm64@2.9.3": optional: true + "@types/body-parser@1.19.6": + dependencies: + "@types/connect": 3.4.38 + "@types/node": 25.5.2 + + "@types/connect@3.4.38": + dependencies: + "@types/node": 25.5.2 + "@types/esrecurse@4.3.1": {} "@types/estree@1.0.8": {} + "@types/express-serve-static-core@5.1.1": + dependencies: + "@types/node": 25.5.2 + "@types/qs": 6.15.0 + "@types/range-parser": 1.2.7 + "@types/send": 1.2.1 + + "@types/express@5.0.6": + dependencies: + "@types/body-parser": 1.19.6 + "@types/express-serve-static-core": 5.1.1 + "@types/serve-static": 2.2.0 + + "@types/http-errors@2.0.5": {} + "@types/json-schema@7.0.15": {} "@types/jsonwebtoken@9.0.10": @@ -3247,10 +3412,43 @@ snapshots: dependencies: undici-types: 7.18.2 + "@types/oauth@0.9.6": + dependencies: + "@types/node": 25.5.2 + + "@types/passport-github2@1.2.9": + dependencies: + "@types/express": 5.0.6 + "@types/passport": 1.0.17 + "@types/passport-oauth2": 1.8.0 + + "@types/passport-oauth2@1.8.0": + dependencies: + "@types/express": 5.0.6 + "@types/oauth": 0.9.6 + "@types/passport": 1.0.17 + + "@types/passport@1.0.17": + dependencies: + "@types/express": 5.0.6 + + "@types/qs@6.15.0": {} + + "@types/range-parser@1.2.7": {} + "@types/react@19.2.14": dependencies: csstype: 3.2.3 + "@types/send@1.2.1": + dependencies: + "@types/node": 25.5.2 + + "@types/serve-static@2.2.0": + dependencies: + "@types/http-errors": 2.0.5 + "@types/node": 25.5.2 + "@types/strip-bom@3.0.0": {} "@types/strip-json-comments@0.0.30": {} @@ -3309,8 +3507,6 @@ snapshots: balanced-match@4.0.4: {} - base64url@3.0.1: {} - better-result@2.7.0: dependencies: "@clack/prompts": 0.11.0 @@ -4007,8 +4203,6 @@ snapshots: pathe: 2.0.3 tinyexec: 1.0.4 - oauth@0.10.2: {} - object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -4036,6 +4230,15 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + otplib@13.4.0: + dependencies: + "@otplib/core": 13.4.0 + "@otplib/hotp": 13.4.0 + "@otplib/plugin-base32-scure": 13.4.0 + "@otplib/plugin-crypto-noble": 13.4.0 + "@otplib/totp": 13.4.0 + "@otplib/uri": 13.4.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -4046,26 +4249,6 @@ snapshots: parseurl@1.3.3: {} - passport-github2@0.1.12: - dependencies: - passport-oauth2: 1.8.0 - - passport-oauth2@1.8.0: - dependencies: - base64url: 3.0.1 - oauth: 0.10.2 - passport-strategy: 1.0.0 - uid2: 0.0.4 - utils-merge: 1.0.1 - - passport-strategy@1.0.0: {} - - passport@0.7.0: - dependencies: - passport-strategy: 1.0.0 - pause: 0.0.1 - utils-merge: 1.0.1 - path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -4078,8 +4261,6 @@ snapshots: pathe@2.0.3: {} - pause@0.0.1: {} - perfect-debounce@1.0.0: {} picocolors@1.1.1: {} @@ -4341,6 +4522,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + thirty-two@1.0.2: {} + tinyexec@1.0.4: {} to-regex-range@5.0.1: @@ -4430,8 +4613,6 @@ snapshots: typescript@5.9.3: {} - uid2@0.0.4: {} - uid@2.0.2: dependencies: "@lukeed/csprng": 1.1.0 @@ -4448,8 +4629,6 @@ snapshots: util-deprecate@1.0.2: {} - utils-merge@1.0.1: {} - v8-compile-cache-lib@3.0.1: {} valibot@1.2.0(typescript@5.9.3): From 8f6ff38a32ad97358d0dacc996d92fb8fea71a15 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sun, 5 Apr 2026 00:01:28 +0800 Subject: [PATCH 17/22] feat(api-task): implement task crud with tag filters --- apps/api/src/app.module.ts | 6 +- apps/api/src/prisma/prisma.module.ts | 9 + apps/api/src/prisma/prisma.service.ts | 13 + apps/api/src/task/dto/create-task.dto.ts | 64 +++ apps/api/src/task/dto/list-tasks-query.dto.ts | 92 +++++ apps/api/src/task/dto/update-task.dto.ts | 65 +++ apps/api/src/task/task.controller.ts | 71 ++++ apps/api/src/task/task.module.ts | 11 + apps/api/src/task/task.service.ts | 390 ++++++++++++++++++ apps/api/tsconfig.json | 4 +- 10 files changed, 722 insertions(+), 3 deletions(-) create mode 100644 apps/api/src/prisma/prisma.module.ts create mode 100644 apps/api/src/prisma/prisma.service.ts create mode 100644 apps/api/src/task/dto/create-task.dto.ts create mode 100644 apps/api/src/task/dto/list-tasks-query.dto.ts create mode 100644 apps/api/src/task/dto/update-task.dto.ts create mode 100644 apps/api/src/task/task.controller.ts create mode 100644 apps/api/src/task/task.module.ts create mode 100644 apps/api/src/task/task.service.ts diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts index 8cd6181..bbf571a 100644 --- a/apps/api/src/app.module.ts +++ b/apps/api/src/app.module.ts @@ -1,6 +1,8 @@ import { Module } from "@nestjs/common"; import { ConfigModule } from "@nestjs/config"; import { AuthModule } from "./auth/auth.module"; +import { PrismaModule } from "./prisma/prisma.module"; +import { TaskModule } from "./task/task.module"; @Module({ imports: [ @@ -8,7 +10,9 @@ import { AuthModule } from "./auth/auth.module"; isGlobal: true, envFilePath: ".env" }), - AuthModule + PrismaModule, + AuthModule, + TaskModule ] }) export class AppModule {} diff --git a/apps/api/src/prisma/prisma.module.ts b/apps/api/src/prisma/prisma.module.ts new file mode 100644 index 0000000..7a94e73 --- /dev/null +++ b/apps/api/src/prisma/prisma.module.ts @@ -0,0 +1,9 @@ +import { Global, Module } from "@nestjs/common"; +import { PrismaService } from "./prisma.service"; + +@Global() +@Module({ + providers: [PrismaService], + exports: [PrismaService] +}) +export class PrismaModule {} diff --git a/apps/api/src/prisma/prisma.service.ts b/apps/api/src/prisma/prisma.service.ts new file mode 100644 index 0000000..72e40f6 --- /dev/null +++ b/apps/api/src/prisma/prisma.service.ts @@ -0,0 +1,13 @@ +import { Injectable, OnModuleDestroy, OnModuleInit } from "@nestjs/common"; +import { PrismaClient } from "../../generated/prisma/client"; + +@Injectable() +export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { + async onModuleInit(): Promise { + await this.$connect(); + } + + async onModuleDestroy(): Promise { + await this.$disconnect(); + } +} diff --git a/apps/api/src/task/dto/create-task.dto.ts b/apps/api/src/task/dto/create-task.dto.ts new file mode 100644 index 0000000..69c2000 --- /dev/null +++ b/apps/api/src/task/dto/create-task.dto.ts @@ -0,0 +1,64 @@ +import { Transform } from "class-transformer"; +import { + IsArray, + IsDateString, + IsEnum, + IsObject, + IsOptional, + IsString, + MaxLength, + MinLength +} from "class-validator"; +import { TaskPriority, TaskStatus } from "../../../generated/prisma/client"; + +function normalizeString(value: unknown): unknown { + if (typeof value !== "string") { + return value; + } + + return value.trim(); +} + +export class CreateTaskDto { + @Transform(({ value }) => normalizeString(value)) + @IsString() + @MinLength(1) + @MaxLength(120) + title!: string; + + @IsOptional() + @IsObject() + contentJson?: Record; + + @Transform(({ value }) => normalizeString(value)) + @IsOptional() + @IsString() + @MaxLength(20000) + contentText?: string; + + @IsOptional() + @IsEnum(TaskPriority) + priority?: TaskPriority; + + @IsOptional() + @IsEnum(TaskStatus) + status?: TaskStatus; + + @IsOptional() + @IsDateString() + ddl?: string; + + @Transform(({ value }) => { + if (!Array.isArray(value)) { + return value; + } + + return value.map((item) => normalizeString(item)); + }) + @IsOptional() + @IsArray() + @IsString({ each: true }) + @MinLength(1, { each: true }) + @MaxLength(30, { each: true }) + tagNames?: string[]; +} diff --git a/apps/api/src/task/dto/list-tasks-query.dto.ts b/apps/api/src/task/dto/list-tasks-query.dto.ts new file mode 100644 index 0000000..baa5afb --- /dev/null +++ b/apps/api/src/task/dto/list-tasks-query.dto.ts @@ -0,0 +1,92 @@ +import { Transform, Type } from "class-transformer"; +import { IsArray, IsEnum, IsInt, IsOptional, IsString, Max, MaxLength, Min } from "class-validator"; +import { TaskPriority, TaskStatus } from "../../../generated/prisma/client"; + +export enum TaskSortBy { + CREATED_AT = "createdAt", + UPDATED_AT = "updatedAt", + DDL = "ddl" +} + +export enum TaskSortOrder { + ASC = "asc", + DESC = "desc" +} + +function normalizeString(value: unknown): string | undefined { + if (typeof value !== "string") { + return undefined; + } + + const normalized = value.trim(); + if (!normalized) { + return undefined; + } + + return normalized; +} + +export class ListTasksQueryDto { + @IsOptional() + @IsEnum(TaskStatus) + status?: TaskStatus; + + @IsOptional() + @IsEnum(TaskPriority) + priority?: TaskPriority; + + @Transform(({ value }) => { + if (value === undefined || value === null || value === "") { + return undefined; + } + + if (Array.isArray(value)) { + const normalized = value + .map((item) => normalizeString(item)) + .filter((item): item is string => item !== undefined); + return normalized.length > 0 ? normalized : undefined; + } + + if (typeof value === "string") { + const normalized = value + .split(",") + .map((item) => normalizeString(item)) + .filter((item): item is string => item !== undefined); + return normalized.length > 0 ? normalized : undefined; + } + + return undefined; + }) + @IsOptional() + @IsArray() + @IsString({ each: true }) + @MaxLength(30, { each: true }) + tags?: string[]; + + @Transform(({ value }) => normalizeString(value)) + @IsOptional() + @IsString() + @MaxLength(120) + keyword?: string; + + @Type(() => Number) + @IsOptional() + @IsInt() + @Min(1) + page?: number; + + @Type(() => Number) + @IsOptional() + @IsInt() + @Min(1) + @Max(100) + pageSize?: number; + + @IsOptional() + @IsEnum(TaskSortBy) + sortBy?: TaskSortBy; + + @IsOptional() + @IsEnum(TaskSortOrder) + sortOrder?: TaskSortOrder; +} diff --git a/apps/api/src/task/dto/update-task.dto.ts b/apps/api/src/task/dto/update-task.dto.ts new file mode 100644 index 0000000..23b8894 --- /dev/null +++ b/apps/api/src/task/dto/update-task.dto.ts @@ -0,0 +1,65 @@ +import { Transform } from "class-transformer"; +import { + IsArray, + IsDateString, + IsEnum, + IsObject, + IsOptional, + IsString, + MaxLength, + MinLength +} from "class-validator"; +import { TaskPriority, TaskStatus } from "../../../generated/prisma/client"; + +function normalizeString(value: unknown): unknown { + if (typeof value !== "string") { + return value; + } + + return value.trim(); +} + +export class UpdateTaskDto { + @Transform(({ value }) => normalizeString(value)) + @IsOptional() + @IsString() + @MinLength(1) + @MaxLength(120) + title?: string; + + @IsOptional() + @IsObject() + contentJson?: Record; + + @Transform(({ value }) => normalizeString(value)) + @IsOptional() + @IsString() + @MaxLength(20000) + contentText?: string; + + @IsOptional() + @IsEnum(TaskPriority) + priority?: TaskPriority; + + @IsOptional() + @IsEnum(TaskStatus) + status?: TaskStatus; + + @IsOptional() + @IsDateString() + ddl?: string; + + @Transform(({ value }) => { + if (!Array.isArray(value)) { + return value; + } + + return value.map((item) => normalizeString(item)); + }) + @IsOptional() + @IsArray() + @IsString({ each: true }) + @MinLength(1, { each: true }) + @MaxLength(30, { each: true }) + tagNames?: string[]; +} diff --git a/apps/api/src/task/task.controller.ts b/apps/api/src/task/task.controller.ts new file mode 100644 index 0000000..33b9710 --- /dev/null +++ b/apps/api/src/task/task.controller.ts @@ -0,0 +1,71 @@ +import { + Body, + Controller, + Delete, + Get, + Headers, + Param, + Patch, + Post, + Query, + UnauthorizedException +} from "@nestjs/common"; +import { CreateTaskDto } from "./dto/create-task.dto"; +import { ListTasksQueryDto } from "./dto/list-tasks-query.dto"; +import { UpdateTaskDto } from "./dto/update-task.dto"; +import { ListTasksResponse, TaskResponse, TaskService } from "./task.service"; + +@Controller("tasks") +export class TaskController { + constructor(private readonly taskService: TaskService) {} + + @Get() + async listTasks( + @Headers("x-user-id") userIdHeader: string | string[] | undefined, + @Query() query: ListTasksQueryDto + ): Promise { + return this.taskService.listTasks(this.resolveUserId(userIdHeader), query); + } + + @Get(":taskId") + async getTaskById( + @Headers("x-user-id") userIdHeader: string | string[] | undefined, + @Param("taskId") taskId: string + ): Promise { + return this.taskService.getTaskById(this.resolveUserId(userIdHeader), taskId); + } + + @Post() + async createTask( + @Headers("x-user-id") userIdHeader: string | string[] | undefined, + @Body() body: CreateTaskDto + ): Promise { + return this.taskService.createTask(this.resolveUserId(userIdHeader), body); + } + + @Patch(":taskId") + async updateTask( + @Headers("x-user-id") userIdHeader: string | string[] | undefined, + @Param("taskId") taskId: string, + @Body() body: UpdateTaskDto + ): Promise { + return this.taskService.updateTask(this.resolveUserId(userIdHeader), taskId, body); + } + + @Delete(":taskId") + async deleteTask( + @Headers("x-user-id") userIdHeader: string | string[] | undefined, + @Param("taskId") taskId: string + ): Promise<{ success: boolean }> { + return this.taskService.deleteTask(this.resolveUserId(userIdHeader), taskId); + } + + private resolveUserId(userIdHeader: string | string[] | undefined): string { + const userId = Array.isArray(userIdHeader) ? userIdHeader[0] : userIdHeader; + if (!userId) { + throw new UnauthorizedException("缺少用户上下文"); + } + + return userId; + } +} diff --git a/apps/api/src/task/task.module.ts b/apps/api/src/task/task.module.ts new file mode 100644 index 0000000..8226093 --- /dev/null +++ b/apps/api/src/task/task.module.ts @@ -0,0 +1,11 @@ +import { Module } from "@nestjs/common"; +import { PrismaModule } from "../prisma/prisma.module"; +import { TaskController } from "./task.controller"; +import { TaskService } from "./task.service"; + +@Module({ + imports: [PrismaModule], + controllers: [TaskController], + providers: [TaskService] +}) +export class TaskModule {} diff --git a/apps/api/src/task/task.service.ts b/apps/api/src/task/task.service.ts new file mode 100644 index 0000000..3f62dea --- /dev/null +++ b/apps/api/src/task/task.service.ts @@ -0,0 +1,390 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { Prisma, TaskPriority, TaskStatus } from "../../generated/prisma/client"; +import { PrismaService } from "../prisma/prisma.service"; +import { CreateTaskDto } from "./dto/create-task.dto"; +import { ListTasksQueryDto, TaskSortBy, TaskSortOrder } from "./dto/list-tasks-query.dto"; +import { UpdateTaskDto } from "./dto/update-task.dto"; + +type TaskEntity = Prisma.TaskGetPayload<{ + include: { + taskTags: { + include: { + tag: { + select: { + name: true; + }; + }; + }; + }; + }; +}>; + +export type TaskResponse = { + id: string; + title: string; + contentJson: unknown | null; + contentText: string | null; + priority: TaskPriority; + status: TaskStatus; + ddl: string | null; + completedAt: string | null; + version: number; + tags: string[]; + createdAt: string; + updatedAt: string; +}; + +export type ListTasksResponse = { + items: TaskResponse[]; + page: number; + pageSize: number; + total: number; +}; + +@Injectable() +export class TaskService { + constructor(private readonly prismaService: PrismaService) {} + + async listTasks(userId: string, query: ListTasksQueryDto): Promise { + const page = query.page ?? 1; + const pageSize = query.pageSize ?? 20; + const skip = (page - 1) * pageSize; + + const where = this.buildWhereInput(userId, query); + const orderBy = this.buildOrderByInput(query); + + const [items, total] = await Promise.all([ + this.prismaService.task.findMany({ + where, + orderBy, + skip, + take: pageSize, + include: { + taskTags: { + include: { + tag: { + select: { + name: true + } + } + } + } + } + }), + this.prismaService.task.count({ where }) + ]); + + return { + items: items.map((item) => this.serializeTask(item)), + page, + pageSize, + total + }; + } + + async getTaskById(userId: string, taskId: string): Promise { + const task = await this.prismaService.task.findFirst({ + where: { + id: taskId, + userId + }, + include: { + taskTags: { + include: { + tag: { + select: { + name: true + } + } + } + } + } + }); + + if (!task) { + throw new NotFoundException("任务不存在"); + } + + return this.serializeTask(task); + } + + async createTask(userId: string, body: CreateTaskDto): Promise { + const tagNames = this.normalizeTagNames(body.tagNames); + const nextStatus = body.status ?? TaskStatus.TODO; + const contentJson = + body.contentJson !== undefined ? (body.contentJson as Prisma.InputJsonValue) : undefined; + + const task = await this.prismaService.$transaction(async (tx) => { + const createdTask = await tx.task.create({ + data: { + userId, + title: body.title, + contentJson, + contentText: body.contentText ?? null, + priority: body.priority ?? TaskPriority.MEDIUM, + status: nextStatus, + ddl: body.ddl ? new Date(body.ddl) : null, + completedAt: nextStatus === TaskStatus.DONE ? new Date() : null + } + }); + + await this.replaceTaskTags(tx, userId, createdTask.id, tagNames); + + return tx.task.findUniqueOrThrow({ + where: { id: createdTask.id }, + include: { + taskTags: { + include: { + tag: { + select: { + name: true + } + } + } + } + } + }); + }); + + return this.serializeTask(task); + } + + async updateTask(userId: string, taskId: string, body: UpdateTaskDto): Promise { + const currentTask = await this.prismaService.task.findFirst({ + where: { + id: taskId, + userId + }, + select: { + id: true, + status: true + } + }); + + if (!currentTask) { + throw new NotFoundException("任务不存在"); + } + + const data: Prisma.TaskUpdateInput = { + version: { + increment: 1 + } + }; + + if (body.title !== undefined) { + data.title = body.title; + } + if (body.contentJson !== undefined) { + data.contentJson = body.contentJson as Prisma.InputJsonValue; + } + if (body.contentText !== undefined) { + data.contentText = body.contentText; + } + if (body.priority !== undefined) { + data.priority = body.priority; + } + if (body.status !== undefined) { + data.status = body.status; + if (body.status === TaskStatus.DONE && currentTask.status !== TaskStatus.DONE) { + data.completedAt = new Date(); + } else if (body.status !== TaskStatus.DONE) { + data.completedAt = null; + } + } + if (body.ddl !== undefined) { + data.ddl = body.ddl ? new Date(body.ddl) : null; + } + + const shouldReplaceTags = body.tagNames !== undefined; + const nextTagNames = this.normalizeTagNames(body.tagNames); + + const task = await this.prismaService.$transaction(async (tx) => { + await tx.task.update({ + where: { id: taskId }, + data + }); + + if (shouldReplaceTags) { + await this.replaceTaskTags(tx, userId, taskId, nextTagNames); + } + + return tx.task.findUniqueOrThrow({ + where: { id: taskId }, + include: { + taskTags: { + include: { + tag: { + select: { + name: true + } + } + } + } + } + }); + }); + + return this.serializeTask(task); + } + + async deleteTask(userId: string, taskId: string): Promise<{ success: boolean }> { + const deleted = await this.prismaService.task.deleteMany({ + where: { + id: taskId, + userId + } + }); + + if (deleted.count === 0) { + throw new NotFoundException("任务不存在"); + } + + return { success: true }; + } + + private buildWhereInput(userId: string, query: ListTasksQueryDto): Prisma.TaskWhereInput { + const where: Prisma.TaskWhereInput = { + userId + }; + + if (query.status !== undefined) { + where.status = query.status; + } + + if (query.priority !== undefined) { + where.priority = query.priority; + } + + if (query.tags !== undefined && query.tags.length > 0) { + where.taskTags = { + some: { + tag: { + name: { + in: query.tags + } + } + } + }; + } + + if (query.keyword !== undefined && query.keyword.length > 0) { + where.OR = [ + { + title: { + contains: query.keyword, + mode: "insensitive" + } + }, + { + contentText: { + contains: query.keyword, + mode: "insensitive" + } + } + ]; + } + + return where; + } + + private buildOrderByInput(query: ListTasksQueryDto): Prisma.TaskOrderByWithRelationInput { + const order: Prisma.SortOrder = + query.sortOrder === TaskSortOrder.ASC ? Prisma.SortOrder.asc : Prisma.SortOrder.desc; + + if (query.sortBy === TaskSortBy.CREATED_AT) { + return { createdAt: order }; + } + + if (query.sortBy === TaskSortBy.DDL) { + return { ddl: order }; + } + + return { updatedAt: order }; + } + + private normalizeTagNames(tagNames: string[] | undefined): string[] { + if (!tagNames) { + return []; + } + + const result: string[] = []; + const uniqueNames = new Set(); + + for (const rawTagName of tagNames) { + const normalized = rawTagName.trim(); + if (!normalized) { + continue; + } + + const uniqueKey = normalized.toLocaleLowerCase(); + if (uniqueNames.has(uniqueKey)) { + continue; + } + + uniqueNames.add(uniqueKey); + result.push(normalized); + } + + return result; + } + + private async replaceTaskTags( + tx: Prisma.TransactionClient, + userId: string, + taskId: string, + tagNames: string[] + ): Promise { + await tx.taskTag.deleteMany({ + where: { + taskId + } + }); + + if (tagNames.length === 0) { + return; + } + + const tags = await Promise.all( + tagNames.map((name) => + tx.tag.upsert({ + where: { + userId_name: { + userId, + name + } + }, + update: {}, + create: { + userId, + name + } + }) + ) + ); + + await tx.taskTag.createMany({ + data: tags.map((tag) => ({ + taskId, + tagId: tag.id + })), + skipDuplicates: true + }); + } + + private serializeTask(task: TaskEntity): TaskResponse { + return { + id: task.id, + title: task.title, + contentJson: task.contentJson, + contentText: task.contentText, + priority: task.priority, + status: task.status, + ddl: task.ddl?.toISOString() ?? null, + completedAt: task.completedAt?.toISOString() ?? null, + version: task.version, + tags: task.taskTags.map((taskTag) => taskTag.tag.name), + createdAt: task.createdAt.toISOString(), + updatedAt: task.updatedAt.toISOString() + }; + } +} diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index a6aded2..17d29bc 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -2,9 +2,9 @@ "$schema": "https://json.schemastore.org/tsconfig", "extends": "../../packages/tsconfig/nest-app.json", "compilerOptions": { - "rootDir": "src", + "rootDir": ".", "outDir": "dist" }, - "include": ["src/**/*.ts"], + "include": ["src/**/*.ts", "generated/prisma/**/*.ts"], "exclude": ["dist", "node_modules"] } From bd3241504f1fb3d012d239809e8b1213b6dae530 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sun, 5 Apr 2026 00:05:39 +0800 Subject: [PATCH 18/22] feat(api-attachment): add minio presigned upload flow --- apps/api/.env.example | 8 + apps/api/package.json | 2 + apps/api/src/app.module.ts | 4 +- .../src/attachment/attachment.controller.ts | 38 + apps/api/src/attachment/attachment.module.ts | 11 + apps/api/src/attachment/attachment.service.ts | 207 +++ .../attachment/dto/complete-attachment.dto.ts | 89 + .../attachment/dto/presign-attachment.dto.ts | 35 + pnpm-lock.yaml | 1512 +++++++++++++++++ 9 files changed, 1905 insertions(+), 1 deletion(-) create mode 100644 apps/api/src/attachment/attachment.controller.ts create mode 100644 apps/api/src/attachment/attachment.module.ts create mode 100644 apps/api/src/attachment/attachment.service.ts create mode 100644 apps/api/src/attachment/dto/complete-attachment.dto.ts create mode 100644 apps/api/src/attachment/dto/presign-attachment.dto.ts diff --git a/apps/api/.env.example b/apps/api/.env.example index 4394718..68af79c 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -17,3 +17,11 @@ OAUTH_WECHAT_CLIENT_SECRET="wechat-client-secret" OAUTH_WECHAT_CALLBACK_URL="http://localhost:3000/auth/oauth/wechat/callback" OAUTH_WECHAT_AUTH_URL="https://open.weixin.qq.com/connect/qrconnect" OAUTH_WECHAT_TOKEN_URL="https://api.weixin.qq.com/sns/oauth2/access_token" +S3_ENDPOINT="http://127.0.0.1:9000" +S3_REGION="us-east-1" +S3_BUCKET="todolist" +S3_ACCESS_KEY_ID="minioadmin" +S3_SECRET_ACCESS_KEY="minioadmin" +S3_FORCE_PATH_STYLE="true" +S3_PRESIGN_EXPIRES_SECONDS="900" +S3_PUBLIC_BASE_URL="http://127.0.0.1:9000" diff --git a/apps/api/package.json b/apps/api/package.json index a1327aa..5cda8d9 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -25,6 +25,8 @@ }, "private": true, "dependencies": { + "@aws-sdk/client-s3": "^3.1024.0", + "@aws-sdk/s3-request-presigner": "^3.1024.0", "@nestjs/common": "^11.1.18", "@nestjs/config": "^4.0.3", "@nestjs/core": "^11.1.18", diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts index bbf571a..4f6ee3f 100644 --- a/apps/api/src/app.module.ts +++ b/apps/api/src/app.module.ts @@ -1,5 +1,6 @@ import { Module } from "@nestjs/common"; import { ConfigModule } from "@nestjs/config"; +import { AttachmentModule } from "./attachment/attachment.module"; import { AuthModule } from "./auth/auth.module"; import { PrismaModule } from "./prisma/prisma.module"; import { TaskModule } from "./task/task.module"; @@ -12,7 +13,8 @@ import { TaskModule } from "./task/task.module"; }), PrismaModule, AuthModule, - TaskModule + TaskModule, + AttachmentModule ] }) export class AppModule {} diff --git a/apps/api/src/attachment/attachment.controller.ts b/apps/api/src/attachment/attachment.controller.ts new file mode 100644 index 0000000..6baec6e --- /dev/null +++ b/apps/api/src/attachment/attachment.controller.ts @@ -0,0 +1,38 @@ +import { Body, Controller, Headers, Post, UnauthorizedException } from "@nestjs/common"; +import { + AttachmentResponse, + AttachmentService, + PresignAttachmentResponse +} from "./attachment.service"; +import { CompleteAttachmentDto } from "./dto/complete-attachment.dto"; +import { PresignAttachmentDto } from "./dto/presign-attachment.dto"; + +@Controller("attachments") +export class AttachmentController { + constructor(private readonly attachmentService: AttachmentService) {} + + @Post("presign") + async presignAttachment( + @Headers("x-user-id") userIdHeader: string | string[] | undefined, + @Body() body: PresignAttachmentDto + ): Promise { + return this.attachmentService.presignAttachment(this.resolveUserId(userIdHeader), body); + } + + @Post("complete") + async completeAttachment( + @Headers("x-user-id") userIdHeader: string | string[] | undefined, + @Body() body: CompleteAttachmentDto + ): Promise { + return this.attachmentService.completeAttachment(this.resolveUserId(userIdHeader), body); + } + + private resolveUserId(userIdHeader: string | string[] | undefined): string { + const userId = Array.isArray(userIdHeader) ? userIdHeader[0] : userIdHeader; + if (!userId) { + throw new UnauthorizedException("缺少用户上下文"); + } + + return userId; + } +} diff --git a/apps/api/src/attachment/attachment.module.ts b/apps/api/src/attachment/attachment.module.ts new file mode 100644 index 0000000..cd8dfe3 --- /dev/null +++ b/apps/api/src/attachment/attachment.module.ts @@ -0,0 +1,11 @@ +import { Module } from "@nestjs/common"; +import { PrismaModule } from "../prisma/prisma.module"; +import { AttachmentController } from "./attachment.controller"; +import { AttachmentService } from "./attachment.service"; + +@Module({ + imports: [PrismaModule], + controllers: [AttachmentController], + providers: [AttachmentService] +}) +export class AttachmentModule {} diff --git a/apps/api/src/attachment/attachment.service.ts b/apps/api/src/attachment/attachment.service.ts new file mode 100644 index 0000000..7ddd5b5 --- /dev/null +++ b/apps/api/src/attachment/attachment.service.ts @@ -0,0 +1,207 @@ +import { randomUUID } from "node:crypto"; +import { Injectable, NotFoundException } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; +import { AttachmentType } from "../../generated/prisma/client"; +import { PrismaService } from "../prisma/prisma.service"; +import { CompleteAttachmentDto } from "./dto/complete-attachment.dto"; +import { PresignAttachmentDto } from "./dto/presign-attachment.dto"; + +export type PresignAttachmentResponse = { + method: "PUT"; + uploadUrl: string; + bucket: string; + objectKey: string; + objectUrl: string; + expiresInSeconds: number; + headers: { + "Content-Type": string; + }; +}; + +export type AttachmentResponse = { + id: string; + taskId: string | null; + type: AttachmentType; + url: string; + mimeType: string | null; + fileName: string | null; + fileSize: number; + width: number | null; + height: number | null; + durationMs: number | null; + checksum: string | null; + createdAt: string; + updatedAt: string; +}; + +@Injectable() +export class AttachmentService { + private s3Client: S3Client | null = null; + + constructor( + private readonly configService: ConfigService, + private readonly prismaService: PrismaService + ) {} + + async presignAttachment( + userId: string, + body: PresignAttachmentDto + ): Promise { + if (body.taskId) { + await this.ensureTaskOwnership(userId, body.taskId); + } + + const bucket = this.getDefaultBucket(); + const objectKey = this.generateObjectKey(userId, body.fileName); + const objectUrl = this.resolveObjectUrl(bucket, objectKey); + const expiresInSeconds = this.getPresignExpiresInSeconds(); + + const command = new PutObjectCommand({ + Bucket: bucket, + Key: objectKey, + ContentType: body.mimeType, + ContentLength: body.fileSize + }); + + const uploadUrl = await getSignedUrl(this.getS3Client(), command, { + expiresIn: expiresInSeconds + }); + + return { + method: "PUT", + uploadUrl, + bucket, + objectKey, + objectUrl, + expiresInSeconds, + headers: { + "Content-Type": body.mimeType + } + }; + } + + async completeAttachment( + userId: string, + body: CompleteAttachmentDto + ): Promise { + if (body.taskId) { + await this.ensureTaskOwnership(userId, body.taskId); + } + + const bucket = body.bucket ?? this.getDefaultBucket(); + const objectUrl = this.resolveObjectUrl(bucket, body.objectKey); + const attachment = await this.prismaService.attachment.create({ + data: { + userId, + taskId: body.taskId ?? null, + type: body.type ?? this.resolveAttachmentType(body.mimeType), + url: objectUrl, + mimeType: body.mimeType, + fileName: body.fileName, + fileSize: body.fileSize, + width: body.width ?? null, + height: body.height ?? null, + durationMs: body.durationMs ?? null, + checksum: body.checksum ?? null + } + }); + + return { + id: attachment.id, + taskId: attachment.taskId, + type: attachment.type, + url: attachment.url, + mimeType: attachment.mimeType, + fileName: attachment.fileName, + fileSize: attachment.fileSize, + width: attachment.width, + height: attachment.height, + durationMs: attachment.durationMs, + checksum: attachment.checksum, + createdAt: attachment.createdAt.toISOString(), + updatedAt: attachment.updatedAt.toISOString() + }; + } + + private getS3Client(): S3Client { + if (this.s3Client) { + return this.s3Client; + } + + const endpoint = this.configService.get("S3_ENDPOINT") ?? "http://127.0.0.1:9000"; + const region = this.configService.get("S3_REGION") ?? "us-east-1"; + const forcePathStyle = + this.configService.get("S3_FORCE_PATH_STYLE")?.toLowerCase() !== "false"; + + this.s3Client = new S3Client({ + endpoint, + region, + forcePathStyle, + credentials: { + accessKeyId: this.configService.get("S3_ACCESS_KEY_ID") ?? "minioadmin", + secretAccessKey: this.configService.get("S3_SECRET_ACCESS_KEY") ?? "minioadmin" + } + }); + + return this.s3Client; + } + + private getDefaultBucket(): string { + return this.configService.get("S3_BUCKET") ?? "todolist"; + } + + private getPresignExpiresInSeconds(): number { + const configValue = Number(this.configService.get("S3_PRESIGN_EXPIRES_SECONDS") ?? 900); + if (!Number.isFinite(configValue) || configValue <= 0) { + return 900; + } + + return Math.min(configValue, 604800); + } + + private generateObjectKey(userId: string, fileName: string): string { + const safeFileName = fileName.replace(/[^\w.-]+/g, "_"); + const datePrefix = new Date().toISOString().slice(0, 10); + return `${userId}/${datePrefix}/${randomUUID()}-${safeFileName}`; + } + + private resolveObjectUrl(bucket: string, objectKey: string): string { + const publicBaseUrl = this.configService.get("S3_PUBLIC_BASE_URL"); + if (publicBaseUrl) { + return `${publicBaseUrl.replace(/\/+$/, "")}/${bucket}/${objectKey}`; + } + + const endpoint = this.configService.get("S3_ENDPOINT") ?? "http://127.0.0.1:9000"; + return `${endpoint.replace(/\/+$/, "")}/${bucket}/${objectKey}`; + } + + private resolveAttachmentType(mimeType: string): AttachmentType { + if (mimeType.startsWith("image/")) { + return AttachmentType.IMAGE; + } + + if (mimeType.startsWith("video/")) { + return AttachmentType.VIDEO; + } + + return AttachmentType.FILE; + } + + private async ensureTaskOwnership(userId: string, taskId: string): Promise { + const task = await this.prismaService.task.findFirst({ + where: { + id: taskId, + userId + }, + select: { + id: true + } + }); + + if (!task) { + throw new NotFoundException("任务不存在"); + } + } +} diff --git a/apps/api/src/attachment/dto/complete-attachment.dto.ts b/apps/api/src/attachment/dto/complete-attachment.dto.ts new file mode 100644 index 0000000..2318e3b --- /dev/null +++ b/apps/api/src/attachment/dto/complete-attachment.dto.ts @@ -0,0 +1,89 @@ +import { Transform, Type } from "class-transformer"; +import { + IsEnum, + IsInt, + IsOptional, + IsString, + Max, + MaxLength, + Min, + MinLength +} from "class-validator"; +import { AttachmentType } from "../../../generated/prisma/client"; + +function normalizeString(value: unknown): unknown { + if (typeof value !== "string") { + return value; + } + + return value.trim(); +} + +export class CompleteAttachmentDto { + @Transform(({ value }) => normalizeString(value)) + @IsString() + @MinLength(1) + @MaxLength(255) + objectKey!: string; + + @Transform(({ value }) => normalizeString(value)) + @IsOptional() + @IsString() + @MaxLength(100) + bucket?: string; + + @Transform(({ value }) => normalizeString(value)) + @IsString() + @MinLength(1) + @MaxLength(255) + fileName!: string; + + @Transform(({ value }) => normalizeString(value)) + @IsString() + @MinLength(1) + @MaxLength(255) + mimeType!: string; + + @Type(() => Number) + @IsInt() + @Min(1) + @Max(1073741824) + fileSize!: number; + + @IsOptional() + @IsEnum(AttachmentType) + type?: AttachmentType; + + @Transform(({ value }) => normalizeString(value)) + @IsOptional() + @IsString() + @MaxLength(255) + taskId?: string; + + @Transform(({ value }) => normalizeString(value)) + @IsOptional() + @IsString() + @MaxLength(128) + checksum?: string; + + @Type(() => Number) + @IsOptional() + @IsInt() + @Min(1) + @Max(100000) + width?: number; + + @Type(() => Number) + @IsOptional() + @IsInt() + @Min(1) + @Max(100000) + height?: number; + + @Type(() => Number) + @IsOptional() + @IsInt() + @Min(1) + @Max(86400000) + durationMs?: number; +} diff --git a/apps/api/src/attachment/dto/presign-attachment.dto.ts b/apps/api/src/attachment/dto/presign-attachment.dto.ts new file mode 100644 index 0000000..9eda1a9 --- /dev/null +++ b/apps/api/src/attachment/dto/presign-attachment.dto.ts @@ -0,0 +1,35 @@ +import { Transform } from "class-transformer"; +import { IsInt, IsOptional, IsString, Max, MaxLength, Min, MinLength } from "class-validator"; + +function normalizeString(value: unknown): unknown { + if (typeof value !== "string") { + return value; + } + + return value.trim(); +} + +export class PresignAttachmentDto { + @Transform(({ value }) => normalizeString(value)) + @IsString() + @MinLength(1) + @MaxLength(255) + fileName!: string; + + @Transform(({ value }) => normalizeString(value)) + @IsString() + @MinLength(1) + @MaxLength(255) + mimeType!: string; + + @IsInt() + @Min(1) + @Max(1073741824) + fileSize!: number; + + @Transform(({ value }) => normalizeString(value)) + @IsOptional() + @IsString() + @MaxLength(255) + taskId?: string; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 051c827..4726407 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,6 +31,12 @@ importers: apps/api: dependencies: + "@aws-sdk/client-s3": + specifier: ^3.1024.0 + version: 3.1024.0 + "@aws-sdk/s3-request-presigner": + specifier: ^3.1024.0 + version: 3.1024.0 "@nestjs/common": specifier: ^11.1.18 version: 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -98,6 +104,299 @@ importers: packages/tsconfig: {} packages: + "@aws-crypto/crc32@5.2.0": + resolution: + { + integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg== + } + engines: { node: ">=16.0.0" } + + "@aws-crypto/crc32c@5.2.0": + resolution: + { + integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag== + } + + "@aws-crypto/sha1-browser@5.2.0": + resolution: + { + integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg== + } + + "@aws-crypto/sha256-browser@5.2.0": + resolution: + { + integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw== + } + + "@aws-crypto/sha256-js@5.2.0": + resolution: + { + integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== + } + engines: { node: ">=16.0.0" } + + "@aws-crypto/supports-web-crypto@5.2.0": + resolution: + { + integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== + } + + "@aws-crypto/util@5.2.0": + resolution: + { + integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== + } + + "@aws-sdk/client-s3@3.1024.0": + resolution: + { + integrity: sha512-8qdO5aLCzaf9l0RdrSBW1iIroRKP2QBqtZ6lkrtHKiaaH0B18xEn+lrEgiN/eCf3uRAYk4cqbnI2XcWzm+7dDQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/core@3.973.26": + resolution: + { + integrity: sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/crc64-nvme@3.972.5": + resolution: + { + integrity: sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/credential-provider-env@3.972.24": + resolution: + { + integrity: sha512-FWg8uFmT6vQM7VuzELzwVo5bzExGaKHdubn0StjgrcU5FvuLExUe+k06kn/40uKv59rYzhez8eFNM4yYE/Yb/w== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/credential-provider-http@3.972.26": + resolution: + { + integrity: sha512-CY4ppZ+qHYqcXqBVi//sdHST1QK3KzOEiLtpLsc9W2k2vfZPKExGaQIsOwcyvjpjUEolotitmd3mUNY56IwDEA== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/credential-provider-ini@3.972.28": + resolution: + { + integrity: sha512-wXYvq3+uQcZV7k+bE4yDXCTBdzWTU9x/nMiKBfzInmv6yYK1veMK0AKvRfRBd72nGWYKcL6AxwiPg9z/pYlgpw== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/credential-provider-login@3.972.28": + resolution: + { + integrity: sha512-ZSTfO6jqUTCysbdBPtEX5OUR//3rbD0lN7jO3sQeS2Gjr/Y+DT6SbIJ0oT2cemNw3UzKu97sNONd1CwNMthuZQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/credential-provider-node@3.972.29": + resolution: + { + integrity: sha512-clSzDcvndpFJAggLDnDb36sPdlZYyEs5Zm6zgZjjUhwsJgSWiWKwFIXUVBcbruidNyBdbpOv2tNDL9sX8y3/0g== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/credential-provider-process@3.972.24": + resolution: + { + integrity: sha512-Q2k/XLrFXhEztPHqj4SLCNID3hEPdlhh1CDLBpNnM+1L8fq7P+yON9/9M1IGN/dA5W45v44ylERfXtDAlmMNmw== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/credential-provider-sso@3.972.28": + resolution: + { + integrity: sha512-IoUlmKMLEITFn1SiCTjPfR6KrE799FBo5baWyk/5Ppar2yXZoUdaRqZzJzK6TcJxx450M8m8DbpddRVYlp5R/A== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/credential-provider-web-identity@3.972.28": + resolution: + { + integrity: sha512-d+6h0SD8GGERzKe27v5rOzNGKOl0D+l0bWJdqrxH8WSQzHzjsQFIAPgIeOTUwBHVsKKwtSxc91K/SWax6XgswQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/middleware-bucket-endpoint@3.972.8": + resolution: + { + integrity: sha512-WR525Rr2QJSETa9a050isktyWi/4yIGcmY3BQ1kpHqb0LqUglQHCS8R27dTJxxWNZvQ0RVGtEZjTCbZJpyF3Aw== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/middleware-expect-continue@3.972.8": + resolution: + { + integrity: sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/middleware-flexible-checksums@3.974.6": + resolution: + { + integrity: sha512-YckB8k1ejbyCg/g36gUMFLNzE4W5cERIa4MtsdO+wpTmJEP0+TB7okWIt7d8TDOvnb7SwvxJ21E4TGOBxFpSWQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/middleware-host-header@3.972.8": + resolution: + { + integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/middleware-location-constraint@3.972.8": + resolution: + { + integrity: sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/middleware-logger@3.972.8": + resolution: + { + integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/middleware-recursion-detection@3.972.9": + resolution: + { + integrity: sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/middleware-sdk-s3@3.972.27": + resolution: + { + integrity: sha512-gomO6DZwx+1D/9mbCpcqO5tPBqYBK7DtdgjTIjZ4yvfh/S7ETwAPS0XbJgP2JD8Ycr5CwVrEkV1sFtu3ShXeOw== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/middleware-ssec@3.972.8": + resolution: + { + integrity: sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/middleware-user-agent@3.972.28": + resolution: + { + integrity: sha512-cfWZFlVh7Va9lRay4PN2A9ARFzaBYcA097InT5M2CdRS05ECF5yaz86jET8Wsl2WcyKYEvVr/QNmKtYtafUHtQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/nested-clients@3.996.18": + resolution: + { + integrity: sha512-c7ZSIXrESxHKx2Mcopgd8AlzZgoXMr20fkx5ViPWPOLBvmyhw9VwJx/Govg8Ef/IhEon5R9l53Z8fdYSEmp6VA== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/region-config-resolver@3.972.10": + resolution: + { + integrity: sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/s3-request-presigner@3.1024.0": + resolution: + { + integrity: sha512-KNoGsXnTfBxPqrtV2Owd4mrLnhTHRsOz6Hdaz+As5czGMQuPchoRvG1BJzn2NFRGLt/HSJAXVn+G6IhtRIDe+Q== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/signature-v4-multi-region@3.996.15": + resolution: + { + integrity: sha512-Ukw2RpqvaL96CjfH/FgfBmy/ZosHBqoHBCFsN61qGg99F33vpntIVii8aNeh65XuOja73arSduskoa4OJea9RQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/token-providers@3.1021.0": + resolution: + { + integrity: sha512-TKY6h9spUk3OLs5v1oAgW9mAeBE3LAGNBwJokLy96wwmd4W2v/tYlXseProyed9ValDj2u1jK/4Rg1T+1NXyJA== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/types@3.973.6": + resolution: + { + integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/util-arn-parser@3.972.3": + resolution: + { + integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/util-endpoints@3.996.5": + resolution: + { + integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/util-format-url@3.972.8": + resolution: + { + integrity: sha512-J6DS9oocrgxM8xlUTTmQOuwRF6rnAGEujAN9SAzllcrQmwn5iJ58ogxy3SEhD0Q7JZvlA5jvIXBkpQRqEqlE9A== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/util-locate-window@3.965.5": + resolution: + { + integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ== + } + engines: { node: ">=20.0.0" } + + "@aws-sdk/util-user-agent-browser@3.972.8": + resolution: + { + integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA== + } + + "@aws-sdk/util-user-agent-node@3.973.14": + resolution: + { + integrity: sha512-vNSB/DYaPOyujVZBg/zUznH9QC142MaTHVmaFlF7uzzfg3CgT9f/l4C0Yi+vU/tbBhxVcXVB90Oohk5+o+ZbWw== + } + engines: { node: ">=20.0.0" } + peerDependencies: + aws-crt: ">=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + + "@aws-sdk/xml-builder@3.972.16": + resolution: + { + integrity: sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A== + } + engines: { node: ">=20.0.0" } + + "@aws/lambda-invoke-store@0.2.4": + resolution: + { + integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ== + } + engines: { node: ">=18.0.0" } + "@borewit/text-codec@0.2.2": resolution: { @@ -622,6 +921,377 @@ packages: integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w== } + "@smithy/chunked-blob-reader-native@4.2.3": + resolution: + { + integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw== + } + engines: { node: ">=18.0.0" } + + "@smithy/chunked-blob-reader@5.2.2": + resolution: + { + integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw== + } + engines: { node: ">=18.0.0" } + + "@smithy/config-resolver@4.4.13": + resolution: + { + integrity: sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg== + } + engines: { node: ">=18.0.0" } + + "@smithy/core@3.23.13": + resolution: + { + integrity: sha512-J+2TT9D6oGsUVXVEMvz8h2EmdVnkBiy2auCie4aSJMvKlzUtO5hqjEzXhoCUkIMo7gAYjbQcN0g/MMSXEhDs1Q== + } + engines: { node: ">=18.0.0" } + + "@smithy/credential-provider-imds@4.2.12": + resolution: + { + integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg== + } + engines: { node: ">=18.0.0" } + + "@smithy/eventstream-codec@4.2.12": + resolution: + { + integrity: sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA== + } + engines: { node: ">=18.0.0" } + + "@smithy/eventstream-serde-browser@4.2.12": + resolution: + { + integrity: sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A== + } + engines: { node: ">=18.0.0" } + + "@smithy/eventstream-serde-config-resolver@4.3.12": + resolution: + { + integrity: sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q== + } + engines: { node: ">=18.0.0" } + + "@smithy/eventstream-serde-node@4.2.12": + resolution: + { + integrity: sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA== + } + engines: { node: ">=18.0.0" } + + "@smithy/eventstream-serde-universal@4.2.12": + resolution: + { + integrity: sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ== + } + engines: { node: ">=18.0.0" } + + "@smithy/fetch-http-handler@5.3.15": + resolution: + { + integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A== + } + engines: { node: ">=18.0.0" } + + "@smithy/hash-blob-browser@4.2.13": + resolution: + { + integrity: sha512-YrF4zWKh+ghLuquldj6e/RzE3xZYL8wIPfkt0MqCRphVICjyyjH8OwKD7LLlKpVEbk4FLizFfC1+gwK6XQdR3g== + } + engines: { node: ">=18.0.0" } + + "@smithy/hash-node@4.2.12": + resolution: + { + integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w== + } + engines: { node: ">=18.0.0" } + + "@smithy/hash-stream-node@4.2.12": + resolution: + { + integrity: sha512-O3YbmGExeafuM/kP7Y8r6+1y0hIh3/zn6GROx0uNlB54K9oihAL75Qtc+jFfLNliTi6pxOAYZrRKD9A7iA6UFw== + } + engines: { node: ">=18.0.0" } + + "@smithy/invalid-dependency@4.2.12": + resolution: + { + integrity: sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g== + } + engines: { node: ">=18.0.0" } + + "@smithy/is-array-buffer@2.2.0": + resolution: + { + integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== + } + engines: { node: ">=14.0.0" } + + "@smithy/is-array-buffer@4.2.2": + resolution: + { + integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow== + } + engines: { node: ">=18.0.0" } + + "@smithy/md5-js@4.2.12": + resolution: + { + integrity: sha512-W/oIpHCpWU2+iAkfZYyGWE+qkpuf3vEXHLxQQDx9FPNZTTdnul0dZ2d/gUFrtQ5je1G2kp4cjG0/24YueG2LbQ== + } + engines: { node: ">=18.0.0" } + + "@smithy/middleware-content-length@4.2.12": + resolution: + { + integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA== + } + engines: { node: ">=18.0.0" } + + "@smithy/middleware-endpoint@4.4.28": + resolution: + { + integrity: sha512-p1gfYpi91CHcs5cBq982UlGlDrxoYUX6XdHSo91cQ2KFuz6QloHosO7Jc60pJiVmkWrKOV8kFYlGFFbQ2WUKKQ== + } + engines: { node: ">=18.0.0" } + + "@smithy/middleware-retry@4.4.46": + resolution: + { + integrity: sha512-SpvWNNOPOrKQGUqZbEPO+es+FRXMWvIyzUKUOYdDgdlA6BdZj/R58p4umoQ76c2oJC44PiM7mKizyyex1IJzow== + } + engines: { node: ">=18.0.0" } + + "@smithy/middleware-serde@4.2.16": + resolution: + { + integrity: sha512-beqfV+RZ9RSv+sQqor3xroUUYgRFCGRw6niGstPG8zO9LgTl0B0MCucxjmrH/2WwksQN7UUgI7KNANoZv+KALA== + } + engines: { node: ">=18.0.0" } + + "@smithy/middleware-stack@4.2.12": + resolution: + { + integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw== + } + engines: { node: ">=18.0.0" } + + "@smithy/node-config-provider@4.3.12": + resolution: + { + integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw== + } + engines: { node: ">=18.0.0" } + + "@smithy/node-http-handler@4.5.1": + resolution: + { + integrity: sha512-ejjxdAXjkPIs9lyYyVutOGNOraqUE9v/NjGMKwwFrfOM354wfSD8lmlj8hVwUzQmlLLF4+udhfCX9Exnbmvfzw== + } + engines: { node: ">=18.0.0" } + + "@smithy/property-provider@4.2.12": + resolution: + { + integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A== + } + engines: { node: ">=18.0.0" } + + "@smithy/protocol-http@5.3.12": + resolution: + { + integrity: sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw== + } + engines: { node: ">=18.0.0" } + + "@smithy/querystring-builder@4.2.12": + resolution: + { + integrity: sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg== + } + engines: { node: ">=18.0.0" } + + "@smithy/querystring-parser@4.2.12": + resolution: + { + integrity: sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw== + } + engines: { node: ">=18.0.0" } + + "@smithy/service-error-classification@4.2.12": + resolution: + { + integrity: sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ== + } + engines: { node: ">=18.0.0" } + + "@smithy/shared-ini-file-loader@4.4.7": + resolution: + { + integrity: sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw== + } + engines: { node: ">=18.0.0" } + + "@smithy/signature-v4@5.3.12": + resolution: + { + integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw== + } + engines: { node: ">=18.0.0" } + + "@smithy/smithy-client@4.12.8": + resolution: + { + integrity: sha512-aJaAX7vHe5i66smoSSID7t4rKY08PbD8EBU7DOloixvhOozfYWdcSYE4l6/tjkZ0vBZhGjheWzB2mh31sLgCMA== + } + engines: { node: ">=18.0.0" } + + "@smithy/types@4.13.1": + resolution: + { + integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g== + } + engines: { node: ">=18.0.0" } + + "@smithy/url-parser@4.2.12": + resolution: + { + integrity: sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-base64@4.3.2": + resolution: + { + integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-body-length-browser@4.2.2": + resolution: + { + integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-body-length-node@4.2.3": + resolution: + { + integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-buffer-from@2.2.0": + resolution: + { + integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== + } + engines: { node: ">=14.0.0" } + + "@smithy/util-buffer-from@4.2.2": + resolution: + { + integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-config-provider@4.2.2": + resolution: + { + integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-defaults-mode-browser@4.3.44": + resolution: + { + integrity: sha512-eZg6XzaCbVr2S5cAErU5eGBDaOVTuTo1I65i4tQcHENRcZ8rMWhQy1DaIYUSLyZjsfXvmCqZrstSMYyGFocvHA== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-defaults-mode-node@4.2.48": + resolution: + { + integrity: sha512-FqOKTlqSaoV3nzO55pMs5NBnZX8EhoI0DGmn9kbYeXWppgHD6dchyuj2HLqp4INJDJbSrj6OFYJkAh/WhSzZPg== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-endpoints@3.3.3": + resolution: + { + integrity: sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-hex-encoding@4.2.2": + resolution: + { + integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-middleware@4.2.12": + resolution: + { + integrity: sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-retry@4.2.13": + resolution: + { + integrity: sha512-qQQsIvL0MGIbUjeSrg0/VlQ3jGNKyM3/2iU3FPNgy01z+Sp4OvcaxbgIoFOTvB61ZoohtutuOvOcgmhbD0katQ== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-stream@4.5.21": + resolution: + { + integrity: sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-uri-escape@4.2.2": + resolution: + { + integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-utf8@2.3.0": + resolution: + { + integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== + } + engines: { node: ">=14.0.0" } + + "@smithy/util-utf8@4.2.2": + resolution: + { + integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw== + } + engines: { node: ">=18.0.0" } + + "@smithy/util-waiter@4.2.14": + resolution: + { + integrity: sha512-2zqq5o/oizvMaFUlNiTyZ7dbgYv1a893aGut2uaxtbzTx/VYYnRxWzDHuD/ftgcw94ffenua+ZNLrbqwUYE+Bg== + } + engines: { node: ">=18.0.0" } + + "@smithy/uuid@1.1.2": + resolution: + { + integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g== + } + engines: { node: ">=18.0.0" } + "@standard-schema/spec@1.1.0": resolution: { @@ -974,6 +1644,12 @@ packages: } engines: { node: ">=18" } + bowser@2.14.1: + resolution: + { + integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg== + } + brace-expansion@1.1.13: resolution: { @@ -1507,6 +2183,19 @@ packages: integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== } + fast-xml-builder@1.1.4: + resolution: + { + integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg== + } + + fast-xml-parser@5.5.8: + resolution: + { + integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ== + } + hasBin: true + file-entry-cache@8.0.0: resolution: { @@ -2249,6 +2938,13 @@ packages: } engines: { node: ">=8" } + path-expression-matcher@1.2.1: + resolution: + { + integrity: sha512-d7gQQmLvAKXKXE2GeP9apIGbMYKz88zWdsn/BN2HRWVQsDFdUY36WSLTY0Jvd4HWi7Fb30gQ62oAOzdgJA6fZw== + } + engines: { node: ">=14.0.0" } + path-is-absolute@1.0.1: resolution: { @@ -2723,6 +3419,12 @@ packages: } engines: { node: ">=0.10.0" } + strnum@2.2.2: + resolution: + { + integrity: sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA== + } + strtok3@10.3.5: resolution: { @@ -2998,6 +3700,468 @@ packages: } snapshots: + "@aws-crypto/crc32@5.2.0": + dependencies: + "@aws-crypto/util": 5.2.0 + "@aws-sdk/types": 3.973.6 + tslib: 2.8.1 + + "@aws-crypto/crc32c@5.2.0": + dependencies: + "@aws-crypto/util": 5.2.0 + "@aws-sdk/types": 3.973.6 + tslib: 2.8.1 + + "@aws-crypto/sha1-browser@5.2.0": + dependencies: + "@aws-crypto/supports-web-crypto": 5.2.0 + "@aws-crypto/util": 5.2.0 + "@aws-sdk/types": 3.973.6 + "@aws-sdk/util-locate-window": 3.965.5 + "@smithy/util-utf8": 2.3.0 + tslib: 2.8.1 + + "@aws-crypto/sha256-browser@5.2.0": + dependencies: + "@aws-crypto/sha256-js": 5.2.0 + "@aws-crypto/supports-web-crypto": 5.2.0 + "@aws-crypto/util": 5.2.0 + "@aws-sdk/types": 3.973.6 + "@aws-sdk/util-locate-window": 3.965.5 + "@smithy/util-utf8": 2.3.0 + tslib: 2.8.1 + + "@aws-crypto/sha256-js@5.2.0": + dependencies: + "@aws-crypto/util": 5.2.0 + "@aws-sdk/types": 3.973.6 + tslib: 2.8.1 + + "@aws-crypto/supports-web-crypto@5.2.0": + dependencies: + tslib: 2.8.1 + + "@aws-crypto/util@5.2.0": + dependencies: + "@aws-sdk/types": 3.973.6 + "@smithy/util-utf8": 2.3.0 + tslib: 2.8.1 + + "@aws-sdk/client-s3@3.1024.0": + dependencies: + "@aws-crypto/sha1-browser": 5.2.0 + "@aws-crypto/sha256-browser": 5.2.0 + "@aws-crypto/sha256-js": 5.2.0 + "@aws-sdk/core": 3.973.26 + "@aws-sdk/credential-provider-node": 3.972.29 + "@aws-sdk/middleware-bucket-endpoint": 3.972.8 + "@aws-sdk/middleware-expect-continue": 3.972.8 + "@aws-sdk/middleware-flexible-checksums": 3.974.6 + "@aws-sdk/middleware-host-header": 3.972.8 + "@aws-sdk/middleware-location-constraint": 3.972.8 + "@aws-sdk/middleware-logger": 3.972.8 + "@aws-sdk/middleware-recursion-detection": 3.972.9 + "@aws-sdk/middleware-sdk-s3": 3.972.27 + "@aws-sdk/middleware-ssec": 3.972.8 + "@aws-sdk/middleware-user-agent": 3.972.28 + "@aws-sdk/region-config-resolver": 3.972.10 + "@aws-sdk/signature-v4-multi-region": 3.996.15 + "@aws-sdk/types": 3.973.6 + "@aws-sdk/util-endpoints": 3.996.5 + "@aws-sdk/util-user-agent-browser": 3.972.8 + "@aws-sdk/util-user-agent-node": 3.973.14 + "@smithy/config-resolver": 4.4.13 + "@smithy/core": 3.23.13 + "@smithy/eventstream-serde-browser": 4.2.12 + "@smithy/eventstream-serde-config-resolver": 4.3.12 + "@smithy/eventstream-serde-node": 4.2.12 + "@smithy/fetch-http-handler": 5.3.15 + "@smithy/hash-blob-browser": 4.2.13 + "@smithy/hash-node": 4.2.12 + "@smithy/hash-stream-node": 4.2.12 + "@smithy/invalid-dependency": 4.2.12 + "@smithy/md5-js": 4.2.12 + "@smithy/middleware-content-length": 4.2.12 + "@smithy/middleware-endpoint": 4.4.28 + "@smithy/middleware-retry": 4.4.46 + "@smithy/middleware-serde": 4.2.16 + "@smithy/middleware-stack": 4.2.12 + "@smithy/node-config-provider": 4.3.12 + "@smithy/node-http-handler": 4.5.1 + "@smithy/protocol-http": 5.3.12 + "@smithy/smithy-client": 4.12.8 + "@smithy/types": 4.13.1 + "@smithy/url-parser": 4.2.12 + "@smithy/util-base64": 4.3.2 + "@smithy/util-body-length-browser": 4.2.2 + "@smithy/util-body-length-node": 4.2.3 + "@smithy/util-defaults-mode-browser": 4.3.44 + "@smithy/util-defaults-mode-node": 4.2.48 + "@smithy/util-endpoints": 3.3.3 + "@smithy/util-middleware": 4.2.12 + "@smithy/util-retry": 4.2.13 + "@smithy/util-stream": 4.5.21 + "@smithy/util-utf8": 4.2.2 + "@smithy/util-waiter": 4.2.14 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + "@aws-sdk/core@3.973.26": + dependencies: + "@aws-sdk/types": 3.973.6 + "@aws-sdk/xml-builder": 3.972.16 + "@smithy/core": 3.23.13 + "@smithy/node-config-provider": 4.3.12 + "@smithy/property-provider": 4.2.12 + "@smithy/protocol-http": 5.3.12 + "@smithy/signature-v4": 5.3.12 + "@smithy/smithy-client": 4.12.8 + "@smithy/types": 4.13.1 + "@smithy/util-base64": 4.3.2 + "@smithy/util-middleware": 4.2.12 + "@smithy/util-utf8": 4.2.2 + tslib: 2.8.1 + + "@aws-sdk/crc64-nvme@3.972.5": + dependencies: + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/credential-provider-env@3.972.24": + dependencies: + "@aws-sdk/core": 3.973.26 + "@aws-sdk/types": 3.973.6 + "@smithy/property-provider": 4.2.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/credential-provider-http@3.972.26": + dependencies: + "@aws-sdk/core": 3.973.26 + "@aws-sdk/types": 3.973.6 + "@smithy/fetch-http-handler": 5.3.15 + "@smithy/node-http-handler": 4.5.1 + "@smithy/property-provider": 4.2.12 + "@smithy/protocol-http": 5.3.12 + "@smithy/smithy-client": 4.12.8 + "@smithy/types": 4.13.1 + "@smithy/util-stream": 4.5.21 + tslib: 2.8.1 + + "@aws-sdk/credential-provider-ini@3.972.28": + dependencies: + "@aws-sdk/core": 3.973.26 + "@aws-sdk/credential-provider-env": 3.972.24 + "@aws-sdk/credential-provider-http": 3.972.26 + "@aws-sdk/credential-provider-login": 3.972.28 + "@aws-sdk/credential-provider-process": 3.972.24 + "@aws-sdk/credential-provider-sso": 3.972.28 + "@aws-sdk/credential-provider-web-identity": 3.972.28 + "@aws-sdk/nested-clients": 3.996.18 + "@aws-sdk/types": 3.973.6 + "@smithy/credential-provider-imds": 4.2.12 + "@smithy/property-provider": 4.2.12 + "@smithy/shared-ini-file-loader": 4.4.7 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + "@aws-sdk/credential-provider-login@3.972.28": + dependencies: + "@aws-sdk/core": 3.973.26 + "@aws-sdk/nested-clients": 3.996.18 + "@aws-sdk/types": 3.973.6 + "@smithy/property-provider": 4.2.12 + "@smithy/protocol-http": 5.3.12 + "@smithy/shared-ini-file-loader": 4.4.7 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + "@aws-sdk/credential-provider-node@3.972.29": + dependencies: + "@aws-sdk/credential-provider-env": 3.972.24 + "@aws-sdk/credential-provider-http": 3.972.26 + "@aws-sdk/credential-provider-ini": 3.972.28 + "@aws-sdk/credential-provider-process": 3.972.24 + "@aws-sdk/credential-provider-sso": 3.972.28 + "@aws-sdk/credential-provider-web-identity": 3.972.28 + "@aws-sdk/types": 3.973.6 + "@smithy/credential-provider-imds": 4.2.12 + "@smithy/property-provider": 4.2.12 + "@smithy/shared-ini-file-loader": 4.4.7 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + "@aws-sdk/credential-provider-process@3.972.24": + dependencies: + "@aws-sdk/core": 3.973.26 + "@aws-sdk/types": 3.973.6 + "@smithy/property-provider": 4.2.12 + "@smithy/shared-ini-file-loader": 4.4.7 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/credential-provider-sso@3.972.28": + dependencies: + "@aws-sdk/core": 3.973.26 + "@aws-sdk/nested-clients": 3.996.18 + "@aws-sdk/token-providers": 3.1021.0 + "@aws-sdk/types": 3.973.6 + "@smithy/property-provider": 4.2.12 + "@smithy/shared-ini-file-loader": 4.4.7 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + "@aws-sdk/credential-provider-web-identity@3.972.28": + dependencies: + "@aws-sdk/core": 3.973.26 + "@aws-sdk/nested-clients": 3.996.18 + "@aws-sdk/types": 3.973.6 + "@smithy/property-provider": 4.2.12 + "@smithy/shared-ini-file-loader": 4.4.7 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + "@aws-sdk/middleware-bucket-endpoint@3.972.8": + dependencies: + "@aws-sdk/types": 3.973.6 + "@aws-sdk/util-arn-parser": 3.972.3 + "@smithy/node-config-provider": 4.3.12 + "@smithy/protocol-http": 5.3.12 + "@smithy/types": 4.13.1 + "@smithy/util-config-provider": 4.2.2 + tslib: 2.8.1 + + "@aws-sdk/middleware-expect-continue@3.972.8": + dependencies: + "@aws-sdk/types": 3.973.6 + "@smithy/protocol-http": 5.3.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/middleware-flexible-checksums@3.974.6": + dependencies: + "@aws-crypto/crc32": 5.2.0 + "@aws-crypto/crc32c": 5.2.0 + "@aws-crypto/util": 5.2.0 + "@aws-sdk/core": 3.973.26 + "@aws-sdk/crc64-nvme": 3.972.5 + "@aws-sdk/types": 3.973.6 + "@smithy/is-array-buffer": 4.2.2 + "@smithy/node-config-provider": 4.3.12 + "@smithy/protocol-http": 5.3.12 + "@smithy/types": 4.13.1 + "@smithy/util-middleware": 4.2.12 + "@smithy/util-stream": 4.5.21 + "@smithy/util-utf8": 4.2.2 + tslib: 2.8.1 + + "@aws-sdk/middleware-host-header@3.972.8": + dependencies: + "@aws-sdk/types": 3.973.6 + "@smithy/protocol-http": 5.3.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/middleware-location-constraint@3.972.8": + dependencies: + "@aws-sdk/types": 3.973.6 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/middleware-logger@3.972.8": + dependencies: + "@aws-sdk/types": 3.973.6 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/middleware-recursion-detection@3.972.9": + dependencies: + "@aws-sdk/types": 3.973.6 + "@aws/lambda-invoke-store": 0.2.4 + "@smithy/protocol-http": 5.3.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/middleware-sdk-s3@3.972.27": + dependencies: + "@aws-sdk/core": 3.973.26 + "@aws-sdk/types": 3.973.6 + "@aws-sdk/util-arn-parser": 3.972.3 + "@smithy/core": 3.23.13 + "@smithy/node-config-provider": 4.3.12 + "@smithy/protocol-http": 5.3.12 + "@smithy/signature-v4": 5.3.12 + "@smithy/smithy-client": 4.12.8 + "@smithy/types": 4.13.1 + "@smithy/util-config-provider": 4.2.2 + "@smithy/util-middleware": 4.2.12 + "@smithy/util-stream": 4.5.21 + "@smithy/util-utf8": 4.2.2 + tslib: 2.8.1 + + "@aws-sdk/middleware-ssec@3.972.8": + dependencies: + "@aws-sdk/types": 3.973.6 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/middleware-user-agent@3.972.28": + dependencies: + "@aws-sdk/core": 3.973.26 + "@aws-sdk/types": 3.973.6 + "@aws-sdk/util-endpoints": 3.996.5 + "@smithy/core": 3.23.13 + "@smithy/protocol-http": 5.3.12 + "@smithy/types": 4.13.1 + "@smithy/util-retry": 4.2.13 + tslib: 2.8.1 + + "@aws-sdk/nested-clients@3.996.18": + dependencies: + "@aws-crypto/sha256-browser": 5.2.0 + "@aws-crypto/sha256-js": 5.2.0 + "@aws-sdk/core": 3.973.26 + "@aws-sdk/middleware-host-header": 3.972.8 + "@aws-sdk/middleware-logger": 3.972.8 + "@aws-sdk/middleware-recursion-detection": 3.972.9 + "@aws-sdk/middleware-user-agent": 3.972.28 + "@aws-sdk/region-config-resolver": 3.972.10 + "@aws-sdk/types": 3.973.6 + "@aws-sdk/util-endpoints": 3.996.5 + "@aws-sdk/util-user-agent-browser": 3.972.8 + "@aws-sdk/util-user-agent-node": 3.973.14 + "@smithy/config-resolver": 4.4.13 + "@smithy/core": 3.23.13 + "@smithy/fetch-http-handler": 5.3.15 + "@smithy/hash-node": 4.2.12 + "@smithy/invalid-dependency": 4.2.12 + "@smithy/middleware-content-length": 4.2.12 + "@smithy/middleware-endpoint": 4.4.28 + "@smithy/middleware-retry": 4.4.46 + "@smithy/middleware-serde": 4.2.16 + "@smithy/middleware-stack": 4.2.12 + "@smithy/node-config-provider": 4.3.12 + "@smithy/node-http-handler": 4.5.1 + "@smithy/protocol-http": 5.3.12 + "@smithy/smithy-client": 4.12.8 + "@smithy/types": 4.13.1 + "@smithy/url-parser": 4.2.12 + "@smithy/util-base64": 4.3.2 + "@smithy/util-body-length-browser": 4.2.2 + "@smithy/util-body-length-node": 4.2.3 + "@smithy/util-defaults-mode-browser": 4.3.44 + "@smithy/util-defaults-mode-node": 4.2.48 + "@smithy/util-endpoints": 3.3.3 + "@smithy/util-middleware": 4.2.12 + "@smithy/util-retry": 4.2.13 + "@smithy/util-utf8": 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + "@aws-sdk/region-config-resolver@3.972.10": + dependencies: + "@aws-sdk/types": 3.973.6 + "@smithy/config-resolver": 4.4.13 + "@smithy/node-config-provider": 4.3.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/s3-request-presigner@3.1024.0": + dependencies: + "@aws-sdk/signature-v4-multi-region": 3.996.15 + "@aws-sdk/types": 3.973.6 + "@aws-sdk/util-format-url": 3.972.8 + "@smithy/middleware-endpoint": 4.4.28 + "@smithy/protocol-http": 5.3.12 + "@smithy/smithy-client": 4.12.8 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/signature-v4-multi-region@3.996.15": + dependencies: + "@aws-sdk/middleware-sdk-s3": 3.972.27 + "@aws-sdk/types": 3.973.6 + "@smithy/protocol-http": 5.3.12 + "@smithy/signature-v4": 5.3.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/token-providers@3.1021.0": + dependencies: + "@aws-sdk/core": 3.973.26 + "@aws-sdk/nested-clients": 3.996.18 + "@aws-sdk/types": 3.973.6 + "@smithy/property-provider": 4.2.12 + "@smithy/shared-ini-file-loader": 4.4.7 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + "@aws-sdk/types@3.973.6": + dependencies: + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/util-arn-parser@3.972.3": + dependencies: + tslib: 2.8.1 + + "@aws-sdk/util-endpoints@3.996.5": + dependencies: + "@aws-sdk/types": 3.973.6 + "@smithy/types": 4.13.1 + "@smithy/url-parser": 4.2.12 + "@smithy/util-endpoints": 3.3.3 + tslib: 2.8.1 + + "@aws-sdk/util-format-url@3.972.8": + dependencies: + "@aws-sdk/types": 3.973.6 + "@smithy/querystring-builder": 4.2.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@aws-sdk/util-locate-window@3.965.5": + dependencies: + tslib: 2.8.1 + + "@aws-sdk/util-user-agent-browser@3.972.8": + dependencies: + "@aws-sdk/types": 3.973.6 + "@smithy/types": 4.13.1 + bowser: 2.14.1 + tslib: 2.8.1 + + "@aws-sdk/util-user-agent-node@3.973.14": + dependencies: + "@aws-sdk/middleware-user-agent": 3.972.28 + "@aws-sdk/types": 3.973.6 + "@smithy/node-config-provider": 4.3.12 + "@smithy/types": 4.13.1 + "@smithy/util-config-provider": 4.2.2 + tslib: 2.8.1 + + "@aws-sdk/xml-builder@3.972.16": + dependencies: + "@smithy/types": 4.13.1 + fast-xml-parser: 5.5.8 + tslib: 2.8.1 + + "@aws/lambda-invoke-store@0.2.4": {} + "@borewit/text-codec@0.2.2": {} "@clack/core@0.5.0": @@ -3334,6 +4498,338 @@ snapshots: "@scure/base@2.0.0": {} + "@smithy/chunked-blob-reader-native@4.2.3": + dependencies: + "@smithy/util-base64": 4.3.2 + tslib: 2.8.1 + + "@smithy/chunked-blob-reader@5.2.2": + dependencies: + tslib: 2.8.1 + + "@smithy/config-resolver@4.4.13": + dependencies: + "@smithy/node-config-provider": 4.3.12 + "@smithy/types": 4.13.1 + "@smithy/util-config-provider": 4.2.2 + "@smithy/util-endpoints": 3.3.3 + "@smithy/util-middleware": 4.2.12 + tslib: 2.8.1 + + "@smithy/core@3.23.13": + dependencies: + "@smithy/protocol-http": 5.3.12 + "@smithy/types": 4.13.1 + "@smithy/url-parser": 4.2.12 + "@smithy/util-base64": 4.3.2 + "@smithy/util-body-length-browser": 4.2.2 + "@smithy/util-middleware": 4.2.12 + "@smithy/util-stream": 4.5.21 + "@smithy/util-utf8": 4.2.2 + "@smithy/uuid": 1.1.2 + tslib: 2.8.1 + + "@smithy/credential-provider-imds@4.2.12": + dependencies: + "@smithy/node-config-provider": 4.3.12 + "@smithy/property-provider": 4.2.12 + "@smithy/types": 4.13.1 + "@smithy/url-parser": 4.2.12 + tslib: 2.8.1 + + "@smithy/eventstream-codec@4.2.12": + dependencies: + "@aws-crypto/crc32": 5.2.0 + "@smithy/types": 4.13.1 + "@smithy/util-hex-encoding": 4.2.2 + tslib: 2.8.1 + + "@smithy/eventstream-serde-browser@4.2.12": + dependencies: + "@smithy/eventstream-serde-universal": 4.2.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/eventstream-serde-config-resolver@4.3.12": + dependencies: + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/eventstream-serde-node@4.2.12": + dependencies: + "@smithy/eventstream-serde-universal": 4.2.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/eventstream-serde-universal@4.2.12": + dependencies: + "@smithy/eventstream-codec": 4.2.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/fetch-http-handler@5.3.15": + dependencies: + "@smithy/protocol-http": 5.3.12 + "@smithy/querystring-builder": 4.2.12 + "@smithy/types": 4.13.1 + "@smithy/util-base64": 4.3.2 + tslib: 2.8.1 + + "@smithy/hash-blob-browser@4.2.13": + dependencies: + "@smithy/chunked-blob-reader": 5.2.2 + "@smithy/chunked-blob-reader-native": 4.2.3 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/hash-node@4.2.12": + dependencies: + "@smithy/types": 4.13.1 + "@smithy/util-buffer-from": 4.2.2 + "@smithy/util-utf8": 4.2.2 + tslib: 2.8.1 + + "@smithy/hash-stream-node@4.2.12": + dependencies: + "@smithy/types": 4.13.1 + "@smithy/util-utf8": 4.2.2 + tslib: 2.8.1 + + "@smithy/invalid-dependency@4.2.12": + dependencies: + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/is-array-buffer@2.2.0": + dependencies: + tslib: 2.8.1 + + "@smithy/is-array-buffer@4.2.2": + dependencies: + tslib: 2.8.1 + + "@smithy/md5-js@4.2.12": + dependencies: + "@smithy/types": 4.13.1 + "@smithy/util-utf8": 4.2.2 + tslib: 2.8.1 + + "@smithy/middleware-content-length@4.2.12": + dependencies: + "@smithy/protocol-http": 5.3.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/middleware-endpoint@4.4.28": + dependencies: + "@smithy/core": 3.23.13 + "@smithy/middleware-serde": 4.2.16 + "@smithy/node-config-provider": 4.3.12 + "@smithy/shared-ini-file-loader": 4.4.7 + "@smithy/types": 4.13.1 + "@smithy/url-parser": 4.2.12 + "@smithy/util-middleware": 4.2.12 + tslib: 2.8.1 + + "@smithy/middleware-retry@4.4.46": + dependencies: + "@smithy/node-config-provider": 4.3.12 + "@smithy/protocol-http": 5.3.12 + "@smithy/service-error-classification": 4.2.12 + "@smithy/smithy-client": 4.12.8 + "@smithy/types": 4.13.1 + "@smithy/util-middleware": 4.2.12 + "@smithy/util-retry": 4.2.13 + "@smithy/uuid": 1.1.2 + tslib: 2.8.1 + + "@smithy/middleware-serde@4.2.16": + dependencies: + "@smithy/core": 3.23.13 + "@smithy/protocol-http": 5.3.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/middleware-stack@4.2.12": + dependencies: + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/node-config-provider@4.3.12": + dependencies: + "@smithy/property-provider": 4.2.12 + "@smithy/shared-ini-file-loader": 4.4.7 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/node-http-handler@4.5.1": + dependencies: + "@smithy/protocol-http": 5.3.12 + "@smithy/querystring-builder": 4.2.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/property-provider@4.2.12": + dependencies: + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/protocol-http@5.3.12": + dependencies: + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/querystring-builder@4.2.12": + dependencies: + "@smithy/types": 4.13.1 + "@smithy/util-uri-escape": 4.2.2 + tslib: 2.8.1 + + "@smithy/querystring-parser@4.2.12": + dependencies: + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/service-error-classification@4.2.12": + dependencies: + "@smithy/types": 4.13.1 + + "@smithy/shared-ini-file-loader@4.4.7": + dependencies: + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/signature-v4@5.3.12": + dependencies: + "@smithy/is-array-buffer": 4.2.2 + "@smithy/protocol-http": 5.3.12 + "@smithy/types": 4.13.1 + "@smithy/util-hex-encoding": 4.2.2 + "@smithy/util-middleware": 4.2.12 + "@smithy/util-uri-escape": 4.2.2 + "@smithy/util-utf8": 4.2.2 + tslib: 2.8.1 + + "@smithy/smithy-client@4.12.8": + dependencies: + "@smithy/core": 3.23.13 + "@smithy/middleware-endpoint": 4.4.28 + "@smithy/middleware-stack": 4.2.12 + "@smithy/protocol-http": 5.3.12 + "@smithy/types": 4.13.1 + "@smithy/util-stream": 4.5.21 + tslib: 2.8.1 + + "@smithy/types@4.13.1": + dependencies: + tslib: 2.8.1 + + "@smithy/url-parser@4.2.12": + dependencies: + "@smithy/querystring-parser": 4.2.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/util-base64@4.3.2": + dependencies: + "@smithy/util-buffer-from": 4.2.2 + "@smithy/util-utf8": 4.2.2 + tslib: 2.8.1 + + "@smithy/util-body-length-browser@4.2.2": + dependencies: + tslib: 2.8.1 + + "@smithy/util-body-length-node@4.2.3": + dependencies: + tslib: 2.8.1 + + "@smithy/util-buffer-from@2.2.0": + dependencies: + "@smithy/is-array-buffer": 2.2.0 + tslib: 2.8.1 + + "@smithy/util-buffer-from@4.2.2": + dependencies: + "@smithy/is-array-buffer": 4.2.2 + tslib: 2.8.1 + + "@smithy/util-config-provider@4.2.2": + dependencies: + tslib: 2.8.1 + + "@smithy/util-defaults-mode-browser@4.3.44": + dependencies: + "@smithy/property-provider": 4.2.12 + "@smithy/smithy-client": 4.12.8 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/util-defaults-mode-node@4.2.48": + dependencies: + "@smithy/config-resolver": 4.4.13 + "@smithy/credential-provider-imds": 4.2.12 + "@smithy/node-config-provider": 4.3.12 + "@smithy/property-provider": 4.2.12 + "@smithy/smithy-client": 4.12.8 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/util-endpoints@3.3.3": + dependencies: + "@smithy/node-config-provider": 4.3.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/util-hex-encoding@4.2.2": + dependencies: + tslib: 2.8.1 + + "@smithy/util-middleware@4.2.12": + dependencies: + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/util-retry@4.2.13": + dependencies: + "@smithy/service-error-classification": 4.2.12 + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/util-stream@4.5.21": + dependencies: + "@smithy/fetch-http-handler": 5.3.15 + "@smithy/node-http-handler": 4.5.1 + "@smithy/types": 4.13.1 + "@smithy/util-base64": 4.3.2 + "@smithy/util-buffer-from": 4.2.2 + "@smithy/util-hex-encoding": 4.2.2 + "@smithy/util-utf8": 4.2.2 + tslib: 2.8.1 + + "@smithy/util-uri-escape@4.2.2": + dependencies: + tslib: 2.8.1 + + "@smithy/util-utf8@2.3.0": + dependencies: + "@smithy/util-buffer-from": 2.2.0 + tslib: 2.8.1 + + "@smithy/util-utf8@4.2.2": + dependencies: + "@smithy/util-buffer-from": 4.2.2 + tslib: 2.8.1 + + "@smithy/util-waiter@4.2.14": + dependencies: + "@smithy/types": 4.13.1 + tslib: 2.8.1 + + "@smithy/uuid@1.1.2": + dependencies: + tslib: 2.8.1 + "@standard-schema/spec@1.1.0": {} "@tokenizer/inflate@0.4.1": @@ -3527,6 +5023,8 @@ snapshots: transitivePeerDependencies: - supports-color + bowser@2.14.1: {} + brace-expansion@1.1.13: dependencies: balanced-match: 1.0.2 @@ -3846,6 +5344,16 @@ snapshots: fast-uri@3.1.0: {} + fast-xml-builder@1.1.4: + dependencies: + path-expression-matcher: 1.2.1 + + fast-xml-parser@5.5.8: + dependencies: + fast-xml-builder: 1.1.4 + path-expression-matcher: 1.2.1 + strnum: 2.2.2 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -4251,6 +5759,8 @@ snapshots: path-exists@4.0.0: {} + path-expression-matcher@1.2.1: {} + path-is-absolute@1.0.1: {} path-key@3.1.1: {} @@ -4516,6 +6026,8 @@ snapshots: strip-json-comments@2.0.1: {} + strnum@2.2.2: {} + strtok3@10.3.5: dependencies: "@tokenizer/token": 0.3.0 From 32022c14374bb36f5a3061bc21563822d0816215 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sun, 5 Apr 2026 00:08:27 +0800 Subject: [PATCH 19/22] feat(api-quota): enforce user storage quota checks --- apps/api/src/attachment/attachment.service.ts | 103 +++++++++++++++--- 1 file changed, 89 insertions(+), 14 deletions(-) diff --git a/apps/api/src/attachment/attachment.service.ts b/apps/api/src/attachment/attachment.service.ts index 7ddd5b5..031cd03 100644 --- a/apps/api/src/attachment/attachment.service.ts +++ b/apps/api/src/attachment/attachment.service.ts @@ -1,5 +1,5 @@ import { randomUUID } from "node:crypto"; -import { Injectable, NotFoundException } from "@nestjs/common"; +import { Injectable, NotFoundException, PayloadTooLargeException } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; @@ -8,6 +8,11 @@ import { PrismaService } from "../prisma/prisma.service"; import { CompleteAttachmentDto } from "./dto/complete-attachment.dto"; import { PresignAttachmentDto } from "./dto/presign-attachment.dto"; +type QuotaInfo = { + totalBytes: bigint; + usedBytes: bigint; +}; + export type PresignAttachmentResponse = { method: "PUT"; uploadUrl: string; @@ -15,6 +20,11 @@ export type PresignAttachmentResponse = { objectKey: string; objectUrl: string; expiresInSeconds: number; + quota: { + totalBytes: string; + usedBytes: string; + remainingBytes: string; + }; headers: { "Content-Type": string; }; @@ -49,6 +59,9 @@ export class AttachmentService { userId: string, body: PresignAttachmentDto ): Promise { + const quotaInfo = await this.getQuotaSnapshot(userId); + this.assertQuotaAvailable(quotaInfo.totalBytes, quotaInfo.usedBytes, body.fileSize); + if (body.taskId) { await this.ensureTaskOwnership(userId, body.taskId); } @@ -76,6 +89,11 @@ export class AttachmentService { objectKey, objectUrl, expiresInSeconds, + quota: { + totalBytes: quotaInfo.totalBytes.toString(), + usedBytes: quotaInfo.usedBytes.toString(), + remainingBytes: (quotaInfo.totalBytes - quotaInfo.usedBytes).toString() + }, headers: { "Content-Type": body.mimeType } @@ -92,20 +110,45 @@ export class AttachmentService { const bucket = body.bucket ?? this.getDefaultBucket(); const objectUrl = this.resolveObjectUrl(bucket, body.objectKey); - const attachment = await this.prismaService.attachment.create({ - data: { - userId, - taskId: body.taskId ?? null, - type: body.type ?? this.resolveAttachmentType(body.mimeType), - url: objectUrl, - mimeType: body.mimeType, - fileName: body.fileName, - fileSize: body.fileSize, - width: body.width ?? null, - height: body.height ?? null, - durationMs: body.durationMs ?? null, - checksum: body.checksum ?? null + + const attachment = await this.prismaService.$transaction(async (tx) => { + const quotaInfo = await this.getQuotaSnapshot(userId, tx); + this.assertQuotaAvailable(quotaInfo.totalBytes, quotaInfo.usedBytes, body.fileSize); + + const uploadBytes = BigInt(body.fileSize); + const maxUsedBeforeUpload = quotaInfo.totalBytes - uploadBytes; + const updatedUser = await tx.user.updateMany({ + where: { + id: userId, + usedStorageBytes: { + lte: maxUsedBeforeUpload + } + }, + data: { + usedStorageBytes: { + increment: uploadBytes + } + } + }); + if (updatedUser.count === 0) { + throw new PayloadTooLargeException("存储配额不足"); } + + return tx.attachment.create({ + data: { + userId, + taskId: body.taskId ?? null, + type: body.type ?? this.resolveAttachmentType(body.mimeType), + url: objectUrl, + mimeType: body.mimeType, + fileName: body.fileName, + fileSize: body.fileSize, + width: body.width ?? null, + height: body.height ?? null, + durationMs: body.durationMs ?? null, + checksum: body.checksum ?? null + } + }); }); return { @@ -204,4 +247,36 @@ export class AttachmentService { throw new NotFoundException("任务不存在"); } } + + private async getQuotaSnapshot( + userId: string, + tx: Pick = this.prismaService + ): Promise { + const user = await tx.user.findUnique({ + where: { + id: userId + }, + select: { + id: true, + defaultStorageQuotaMb: true, + usedStorageBytes: true + } + }); + + if (!user) { + throw new NotFoundException("用户不存在"); + } + + return { + totalBytes: BigInt(user.defaultStorageQuotaMb) * 1024n * 1024n, + usedBytes: user.usedStorageBytes + }; + } + + private assertQuotaAvailable(totalBytes: bigint, usedBytes: bigint, fileSize: number): void { + const uploadBytes = BigInt(fileSize); + if (uploadBytes > totalBytes || usedBytes + uploadBytes > totalBytes) { + throw new PayloadTooLargeException("存储配额不足"); + } + } } From 3a9b5fb0005888be898c758a5b3668957d875a18 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sun, 5 Apr 2026 00:13:19 +0800 Subject: [PATCH 20/22] test(api-task): add integration tests for task endpoints --- apps/api/jest.config.cjs | 11 + apps/api/package.json | 8 +- apps/api/test/task.spec.ts | 464 +++++ apps/api/tsconfig.spec.json | 9 + pnpm-lock.yaml | 3574 +++++++++++++++++++++++++++++++++++ 5 files changed, 4065 insertions(+), 1 deletion(-) create mode 100644 apps/api/jest.config.cjs create mode 100644 apps/api/test/task.spec.ts create mode 100644 apps/api/tsconfig.spec.json diff --git a/apps/api/jest.config.cjs b/apps/api/jest.config.cjs new file mode 100644 index 0000000..8194a11 --- /dev/null +++ b/apps/api/jest.config.cjs @@ -0,0 +1,11 @@ +/** @type {import('jest').Config} */ +module.exports = { + rootDir: ".", + testEnvironment: "node", + clearMocks: true, + testMatch: ["/test/**/*.spec.ts"], + moduleFileExtensions: ["ts", "js", "json"], + transform: { + "^.+\\.(t|j)s$": ["ts-jest", { tsconfig: "/tsconfig.spec.json" }] + } +}; diff --git a/apps/api/package.json b/apps/api/package.json index 5cda8d9..2b3fe6d 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -10,15 +10,21 @@ "start:dev": "ts-node-dev --respawn --transpile-only src/main.ts", "build": "tsc -p tsconfig.build.json", "typecheck": "tsc --noEmit -p tsconfig.json", - "test": "echo api tests pending" + "test": "jest --config jest.config.cjs --runInBand" }, "license": "GPL-3.0-or-later", "devDependencies": { + "@nestjs/testing": "^11.1.18", + "@types/jest": "^30.0.0", "@types/node": "^25.5.2", "@types/passport-github2": "^1.2.9", "@types/passport-oauth2": "^1.8.0", + "@types/supertest": "^7.2.0", "dotenv": "^16.6.1", + "jest": "^30.3.0", "prisma": "^7.6.0", + "supertest": "^7.2.2", + "ts-jest": "^29.4.9", "ts-node": "^10.9.2", "ts-node-dev": "^2.0.0", "typescript": "^5.9.3" diff --git a/apps/api/test/task.spec.ts b/apps/api/test/task.spec.ts new file mode 100644 index 0000000..fcc6799 --- /dev/null +++ b/apps/api/test/task.spec.ts @@ -0,0 +1,464 @@ +import request from "supertest"; +import { INestApplication, ValidationPipe } from "@nestjs/common"; +import { Test, TestingModule } from "@nestjs/testing"; +import { PrismaService } from "../src/prisma/prisma.service"; +import { TaskController } from "../src/task/task.controller"; +import { TaskService } from "../src/task/task.service"; +import { TaskPriority, TaskStatus } from "../generated/prisma/client"; + +type TaskRecord = { + id: string; + userId: string; + title: string; + contentJson: unknown | null; + contentText: string | null; + priority: TaskPriority; + status: TaskStatus; + ddl: Date | null; + completedAt: Date | null; + version: number; + createdAt: Date; + updatedAt: Date; +}; + +type TagRecord = { + id: string; + userId: string; + name: string; +}; + +type TaskTagRecord = { + taskId: string; + tagId: string; +}; + +type ListWhereInput = { + userId?: string; + status?: TaskStatus; + priority?: TaskPriority; + taskTags?: { + some: { + tag: { + name: { + in: string[]; + }; + }; + }; + }; + OR?: Array<{ + title?: { + contains: string; + mode?: "insensitive"; + }; + contentText?: { + contains: string; + mode?: "insensitive"; + }; + }>; +}; + +class InMemoryPrismaService { + private taskIdSequence = 1; + private tagIdSequence = 1; + private tasks: TaskRecord[] = []; + private tags: TagRecord[] = []; + private taskTags: TaskTagRecord[] = []; + + readonly task = { + findMany: async (args: { + where?: ListWhereInput; + orderBy?: { createdAt?: "asc" | "desc"; updatedAt?: "asc" | "desc"; ddl?: "asc" | "desc" }; + skip?: number; + take?: number; + }) => { + const where = args.where; + const skip = args.skip ?? 0; + const take = args.take ?? 20; + let filtered = [...this.tasks]; + + if (where?.userId) { + filtered = filtered.filter((task) => task.userId === where.userId); + } + if (where?.status) { + filtered = filtered.filter((task) => task.status === where.status); + } + if (where?.priority) { + filtered = filtered.filter((task) => task.priority === where.priority); + } + if (where?.taskTags?.some.tag.name.in) { + const expectedTags = new Set(where.taskTags.some.tag.name.in); + filtered = filtered.filter((task) => { + const taskTagNames = this.getTaskTagNames(task.id); + return taskTagNames.some((tagName) => expectedTags.has(tagName)); + }); + } + if (where?.OR && where.OR.length > 0) { + filtered = filtered.filter((task) => + where.OR!.some((orCondition) => { + if (orCondition.title?.contains) { + return task.title.toLowerCase().includes(orCondition.title.contains.toLowerCase()); + } + if (orCondition.contentText?.contains) { + return ( + task.contentText + ?.toLowerCase() + .includes(orCondition.contentText.contains.toLowerCase()) ?? false + ); + } + return false; + }) + ); + } + + if (args.orderBy) { + const [orderField, orderDirection] = Object.entries(args.orderBy)[0] as [ + "createdAt" | "updatedAt" | "ddl", + "asc" | "desc" + ]; + filtered.sort((left, right) => { + const leftValue = left[orderField]; + const rightValue = right[orderField]; + + if (leftValue === null && rightValue === null) { + return 0; + } + if (leftValue === null) { + return 1; + } + if (rightValue === null) { + return -1; + } + + const diff = leftValue.getTime() - rightValue.getTime(); + return orderDirection === "asc" ? diff : -diff; + }); + } + + return filtered.slice(skip, skip + take).map((task) => this.toTaskWithTags(task)); + }, + + count: async (args: { where?: ListWhereInput }) => { + const results = await this.task.findMany({ + where: args.where, + skip: 0, + take: Number.MAX_SAFE_INTEGER + }); + return results.length; + }, + + findFirst: async (args: { + where: { + id?: string; + userId?: string; + }; + select?: { + id?: boolean; + status?: boolean; + }; + }) => { + const task = this.tasks.find( + (item) => + (args.where.id === undefined || item.id === args.where.id) && + (args.where.userId === undefined || item.userId === args.where.userId) + ); + if (!task) { + return null; + } + + if (args.select) { + return { + id: args.select.id ? task.id : undefined, + status: args.select.status ? task.status : undefined + }; + } + + return this.toTaskWithTags(task); + }, + + create: async (args: { + data: { + userId: string; + title: string; + contentJson?: unknown; + contentText: string | null; + priority: TaskPriority; + status: TaskStatus; + ddl: Date | null; + completedAt: Date | null; + }; + }) => { + const now = new Date(); + const task: TaskRecord = { + id: `task_${this.taskIdSequence++}`, + userId: args.data.userId, + title: args.data.title, + contentJson: args.data.contentJson ?? null, + contentText: args.data.contentText, + priority: args.data.priority, + status: args.data.status, + ddl: args.data.ddl, + completedAt: args.data.completedAt, + version: 1, + createdAt: now, + updatedAt: now + }; + this.tasks.push(task); + return task; + }, + + update: async (args: { + where: { + id: string; + }; + data: { + title?: string; + contentJson?: unknown; + contentText?: string | null; + priority?: TaskPriority; + status?: TaskStatus; + ddl?: Date | null; + completedAt?: Date | null; + version?: { + increment: number; + }; + }; + }) => { + const task = this.tasks.find((item) => item.id === args.where.id); + if (!task) { + throw new Error("task not found"); + } + + if (args.data.title !== undefined) { + task.title = args.data.title; + } + if (args.data.contentJson !== undefined) { + task.contentJson = args.data.contentJson; + } + if (args.data.contentText !== undefined) { + task.contentText = args.data.contentText; + } + if (args.data.priority !== undefined) { + task.priority = args.data.priority; + } + if (args.data.status !== undefined) { + task.status = args.data.status; + } + if (args.data.ddl !== undefined) { + task.ddl = args.data.ddl; + } + if (args.data.completedAt !== undefined) { + task.completedAt = args.data.completedAt; + } + if (args.data.version !== undefined) { + task.version += args.data.version.increment; + } + task.updatedAt = new Date(); + + return task; + }, + + deleteMany: async (args: { + where: { + id: string; + userId: string; + }; + }) => { + const beforeCount = this.tasks.length; + this.tasks = this.tasks.filter( + (task) => !(task.id === args.where.id && task.userId === args.where.userId) + ); + this.taskTags = this.taskTags.filter((taskTag) => taskTag.taskId !== args.where.id); + return { + count: beforeCount - this.tasks.length + }; + }, + + findUniqueOrThrow: async (args: { + where: { + id: string; + }; + }) => { + const task = this.tasks.find((item) => item.id === args.where.id); + if (!task) { + throw new Error("task not found"); + } + + return this.toTaskWithTags(task); + } + }; + + readonly tag = { + upsert: async (args: { + where: { + userId_name: { + userId: string; + name: string; + }; + }; + create: { + userId: string; + name: string; + }; + }) => { + const existing = this.tags.find( + (tag) => + tag.userId === args.where.userId_name.userId && tag.name === args.where.userId_name.name + ); + if (existing) { + return existing; + } + + const createdTag: TagRecord = { + id: `tag_${this.tagIdSequence++}`, + userId: args.create.userId, + name: args.create.name + }; + this.tags.push(createdTag); + return createdTag; + } + }; + + readonly taskTag = { + deleteMany: async (args: { + where: { + taskId: string; + }; + }) => { + const beforeCount = this.taskTags.length; + this.taskTags = this.taskTags.filter((taskTag) => taskTag.taskId !== args.where.taskId); + return { + count: beforeCount - this.taskTags.length + }; + }, + + createMany: async (args: { + data: Array<{ + taskId: string; + tagId: string; + }>; + }) => { + for (const row of args.data) { + const existing = this.taskTags.find( + (taskTag) => taskTag.taskId === row.taskId && taskTag.tagId === row.tagId + ); + if (!existing) { + this.taskTags.push(row); + } + } + return { + count: args.data.length + }; + } + }; + + async $transaction(runner: (tx: InMemoryPrismaService) => Promise): Promise { + return runner(this); + } + + private toTaskWithTags( + task: TaskRecord + ): TaskRecord & { taskTags: Array<{ tag: { name: string } }> } { + return { + ...task, + taskTags: this.taskTags + .filter((taskTag) => taskTag.taskId === task.id) + .map((taskTag) => this.tags.find((tag) => tag.id === taskTag.tagId)) + .filter((tag): tag is TagRecord => tag !== undefined) + .map((tag) => ({ + tag: { + name: tag.name + } + })) + }; + } + + private getTaskTagNames(taskId: string): string[] { + return this.taskTags + .filter((taskTag) => taskTag.taskId === taskId) + .map((taskTag) => this.tags.find((tag) => tag.id === taskTag.tagId)) + .filter((tag): tag is TagRecord => tag !== undefined) + .map((tag) => tag.name); + } +} + +describe("TaskController (integration)", () => { + let app: INestApplication; + const prismaService = new InMemoryPrismaService(); + + beforeAll(async () => { + const moduleRef: TestingModule = await Test.createTestingModule({ + controllers: [TaskController], + providers: [ + TaskService, + { provide: PrismaService, useValue: prismaService as unknown as PrismaService } + ] + }).compile(); + + app = moduleRef.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + forbidNonWhitelisted: true + }) + ); + + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + it("should create, query, update and delete a task", async () => { + const createResponse = await request(app.getHttpServer()) + .post("/tasks") + .set("x-user-id", "user_1") + .send({ + title: "准备周会", + contentText: "整理本周进度", + priority: "HIGH", + tagNames: ["工作", "会议"] + }) + .expect(201); + + expect(createResponse.body.id).toBeDefined(); + expect(createResponse.body.tags).toEqual(["工作", "会议"]); + const taskId = createResponse.body.id as string; + + const listResponse = await request(app.getHttpServer()) + .get("/tasks") + .set("x-user-id", "user_1") + .query({ tags: "会议" }) + .expect(200); + + expect(listResponse.body.total).toBe(1); + expect(listResponse.body.items[0].id).toBe(taskId); + + const updateResponse = await request(app.getHttpServer()) + .patch(`/tasks/${taskId}`) + .set("x-user-id", "user_1") + .send({ + status: "DONE" + }) + .expect(200); + + expect(updateResponse.body.status).toBe("DONE"); + expect(updateResponse.body.completedAt).toBeTruthy(); + expect(updateResponse.body.version).toBe(2); + + await request(app.getHttpServer()) + .delete(`/tasks/${taskId}`) + .set("x-user-id", "user_1") + .expect(200) + .expect({ + success: true + }); + + const listAfterDeleteResponse = await request(app.getHttpServer()) + .get("/tasks") + .set("x-user-id", "user_1") + .expect(200); + expect(listAfterDeleteResponse.body.total).toBe(0); + }); +}); diff --git a/apps/api/tsconfig.spec.json b/apps/api/tsconfig.spec.json new file mode 100644 index 0000000..e83c972 --- /dev/null +++ b/apps/api/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["node", "jest"] + }, + "include": ["src/**/*.ts", "generated/prisma/**/*.ts", "test/**/*.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4726407..a87e0fd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,12 @@ importers: specifier: ^7.8.2 version: 7.8.2 devDependencies: + "@nestjs/testing": + specifier: ^11.1.18 + version: 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(@nestjs/platform-express@11.1.18) + "@types/jest": + specifier: ^30.0.0 + version: 30.0.0 "@types/node": specifier: ^25.5.2 version: 25.5.2 @@ -83,12 +89,24 @@ importers: "@types/passport-oauth2": specifier: ^1.8.0 version: 1.8.0 + "@types/supertest": + specifier: ^7.2.0 + version: 7.2.0 dotenv: specifier: ^16.6.1 version: 16.6.1 + jest: + specifier: ^30.3.0 + version: 30.3.0(@types/node@25.5.2)(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)) prisma: specifier: ^7.6.0 version: 7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + supertest: + specifier: ^7.2.2 + version: 7.2.2 + ts-jest: + specifier: ^29.4.9 + version: 29.4.9(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.5.2)(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)))(typescript@5.9.3) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@25.5.2)(typescript@5.9.3) @@ -397,6 +415,276 @@ packages: } engines: { node: ">=18.0.0" } + "@babel/code-frame@7.29.0": + resolution: + { + integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== + } + engines: { node: ">=6.9.0" } + + "@babel/compat-data@7.29.0": + resolution: + { + integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== + } + engines: { node: ">=6.9.0" } + + "@babel/core@7.29.0": + resolution: + { + integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== + } + engines: { node: ">=6.9.0" } + + "@babel/generator@7.29.1": + resolution: + { + integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== + } + engines: { node: ">=6.9.0" } + + "@babel/helper-compilation-targets@7.28.6": + resolution: + { + integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== + } + engines: { node: ">=6.9.0" } + + "@babel/helper-globals@7.28.0": + resolution: + { + integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== + } + engines: { node: ">=6.9.0" } + + "@babel/helper-module-imports@7.28.6": + resolution: + { + integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== + } + engines: { node: ">=6.9.0" } + + "@babel/helper-module-transforms@7.28.6": + resolution: + { + integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== + } + engines: { node: ">=6.9.0" } + peerDependencies: + "@babel/core": ^7.0.0 + + "@babel/helper-plugin-utils@7.28.6": + resolution: + { + integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug== + } + engines: { node: ">=6.9.0" } + + "@babel/helper-string-parser@7.27.1": + resolution: + { + integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + } + engines: { node: ">=6.9.0" } + + "@babel/helper-validator-identifier@7.28.5": + resolution: + { + integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + } + engines: { node: ">=6.9.0" } + + "@babel/helper-validator-option@7.27.1": + resolution: + { + integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== + } + engines: { node: ">=6.9.0" } + + "@babel/helpers@7.29.2": + resolution: + { + integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw== + } + engines: { node: ">=6.9.0" } + + "@babel/parser@7.29.2": + resolution: + { + integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA== + } + engines: { node: ">=6.0.0" } + hasBin: true + + "@babel/plugin-syntax-async-generators@7.8.4": + resolution: + { + integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-bigint@7.8.3": + resolution: + { + integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-class-properties@7.12.13": + resolution: + { + integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-class-static-block@7.14.5": + resolution: + { + integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + } + engines: { node: ">=6.9.0" } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-import-attributes@7.28.6": + resolution: + { + integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw== + } + engines: { node: ">=6.9.0" } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-import-meta@7.10.4": + resolution: + { + integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-json-strings@7.8.3": + resolution: + { + integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-jsx@7.28.6": + resolution: + { + integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w== + } + engines: { node: ">=6.9.0" } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-logical-assignment-operators@7.10.4": + resolution: + { + integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-nullish-coalescing-operator@7.8.3": + resolution: + { + integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-numeric-separator@7.10.4": + resolution: + { + integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-object-rest-spread@7.8.3": + resolution: + { + integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-optional-catch-binding@7.8.3": + resolution: + { + integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-optional-chaining@7.8.3": + resolution: + { + integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-private-property-in-object@7.14.5": + resolution: + { + integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + } + engines: { node: ">=6.9.0" } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-top-level-await@7.14.5": + resolution: + { + integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + } + engines: { node: ">=6.9.0" } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/plugin-syntax-typescript@7.28.6": + resolution: + { + integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A== + } + engines: { node: ">=6.9.0" } + peerDependencies: + "@babel/core": ^7.0.0-0 + + "@babel/template@7.28.6": + resolution: + { + integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== + } + engines: { node: ">=6.9.0" } + + "@babel/traverse@7.29.0": + resolution: + { + integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== + } + engines: { node: ">=6.9.0" } + + "@babel/types@7.29.0": + resolution: + { + integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== + } + engines: { node: ">=6.9.0" } + + "@bcoe/v8-coverage@0.2.3": + resolution: + { + integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + } + "@borewit/text-codec@0.2.2": resolution: { @@ -445,6 +733,24 @@ packages: integrity: sha512-mZ9NzzUSYPOCnxHH1oAHPRzoMFJHY472raDKwXl/+6oPbpdJ7g8LsCN4FSaIIfkiCKHhb3iF/Zqo3NYxaIhU7Q== } + "@emnapi/core@1.9.2": + resolution: + { + integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA== + } + + "@emnapi/runtime@1.9.2": + resolution: + { + integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw== + } + + "@emnapi/wasi-threads@1.2.1": + resolution: + { + integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w== + } + "@eslint-community/eslint-utils@4.9.1": resolution: { @@ -545,6 +851,175 @@ packages: } engines: { node: ">=18.18" } + "@isaacs/cliui@8.0.2": + resolution: + { + integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + } + engines: { node: ">=12" } + + "@istanbuljs/load-nyc-config@1.1.0": + resolution: + { + integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + } + engines: { node: ">=8" } + + "@istanbuljs/schema@0.1.3": + resolution: + { + integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + } + engines: { node: ">=8" } + + "@jest/console@30.3.0": + resolution: + { + integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/core@30.3.0": + resolution: + { + integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + "@jest/diff-sequences@30.3.0": + resolution: + { + integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/environment@30.3.0": + resolution: + { + integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/expect-utils@30.3.0": + resolution: + { + integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/expect@30.3.0": + resolution: + { + integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/fake-timers@30.3.0": + resolution: + { + integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/get-type@30.1.0": + resolution: + { + integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/globals@30.3.0": + resolution: + { + integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/pattern@30.0.1": + resolution: + { + integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/reporters@30.3.0": + resolution: + { + integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + "@jest/schemas@30.0.5": + resolution: + { + integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/snapshot-utils@30.3.0": + resolution: + { + integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/source-map@30.0.1": + resolution: + { + integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/test-result@30.3.0": + resolution: + { + integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/test-sequencer@30.3.0": + resolution: + { + integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/transform@30.3.0": + resolution: + { + integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jest/types@30.3.0": + resolution: + { + integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + "@jridgewell/gen-mapping@0.3.13": + resolution: + { + integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + } + + "@jridgewell/remapping@2.3.5": + resolution: + { + integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== + } + "@jridgewell/resolve-uri@3.1.2": resolution: { @@ -558,6 +1033,12 @@ packages: integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== } + "@jridgewell/trace-mapping@0.3.31": + resolution: + { + integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + } + "@jridgewell/trace-mapping@0.3.9": resolution: { @@ -577,6 +1058,12 @@ packages: } engines: { node: ">=8" } + "@napi-rs/wasm-runtime@0.2.12": + resolution: + { + integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ== + } + "@nestjs/common@11.1.18": resolution: { @@ -640,6 +1127,29 @@ packages: "@nestjs/common": ^11.0.0 "@nestjs/core": ^11.0.0 + "@nestjs/testing@11.1.18": + resolution: + { + integrity: sha512-frzwNlpBgtAzI3hp/qo57DZoRO4RMTH1wST3QUYEhRTHyfPkLpzkWz3jV/mhApXjD0yT56Ptlzn6zuYPLh87Lw== + } + peerDependencies: + "@nestjs/common": ^11.0.0 + "@nestjs/core": ^11.0.0 + "@nestjs/microservices": ^11.0.0 + "@nestjs/platform-express": ^11.0.0 + peerDependenciesMeta: + "@nestjs/microservices": + optional: true + "@nestjs/platform-express": + optional: true + + "@noble/hashes@1.8.0": + resolution: + { + integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== + } + engines: { node: ^14.21.3 || >=16 } + "@noble/hashes@2.0.1": resolution: { @@ -718,6 +1228,26 @@ packages: integrity: sha512-x1ozBa5bPbdZCrrTL/HK21qchiK7jYElTu+0ft22abeEhiLYgH1+SIULvOcVk3CK8YwF4kdcidvkq4ciejucJA== } + "@paralleldrive/cuid2@2.3.1": + resolution: + { + integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw== + } + + "@pkgjs/parseargs@0.11.0": + resolution: + { + integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + } + engines: { node: ">=14" } + + "@pkgr/core@0.2.9": + resolution: + { + integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== + } + engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } + "@prisma/client-runtime-utils@7.6.0": resolution: { @@ -921,6 +1451,24 @@ packages: integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w== } + "@sinclair/typebox@0.34.49": + resolution: + { + integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A== + } + + "@sinonjs/commons@3.0.1": + resolution: + { + integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + } + + "@sinonjs/fake-timers@15.3.0": + resolution: + { + integrity: sha512-m2xozxSfCIxjDdvbhIWazlP2i2aha/iUmbl94alpsIbd3iLTfeXgfBVbwyWogB6l++istyGZqamgA/EcqYf+Bg== + } + "@smithy/chunked-blob-reader-native@4.2.3": resolution: { @@ -1383,6 +1931,36 @@ packages: cpu: [arm64] os: [win32] + "@tybys/wasm-util@0.10.1": + resolution: + { + integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg== + } + + "@types/babel__core@7.20.5": + resolution: + { + integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + } + + "@types/babel__generator@7.27.0": + resolution: + { + integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== + } + + "@types/babel__template@7.4.4": + resolution: + { + integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + } + + "@types/babel__traverse@7.28.0": + resolution: + { + integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== + } + "@types/body-parser@1.19.6": resolution: { @@ -1395,6 +1973,12 @@ packages: integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== } + "@types/cookiejar@2.1.5": + resolution: + { + integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q== + } + "@types/esrecurse@4.3.1": resolution: { @@ -1425,6 +2009,30 @@ packages: integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== } + "@types/istanbul-lib-coverage@2.0.6": + resolution: + { + integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + } + + "@types/istanbul-lib-report@3.0.3": + resolution: + { + integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + } + + "@types/istanbul-reports@3.0.4": + resolution: + { + integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + } + + "@types/jest@30.0.0": + resolution: + { + integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA== + } + "@types/json-schema@7.0.15": resolution: { @@ -1437,6 +2045,12 @@ packages: integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA== } + "@types/methods@1.1.4": + resolution: + { + integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ== + } + "@types/ms@2.1.0": resolution: { @@ -1503,6 +2117,12 @@ packages: integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ== } + "@types/stack-utils@2.0.3": + resolution: + { + integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + } + "@types/strip-bom@3.0.0": resolution: { @@ -1515,12 +2135,194 @@ packages: integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== } + "@types/superagent@8.1.9": + resolution: + { + integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ== + } + + "@types/supertest@7.2.0": + resolution: + { + integrity: sha512-uh2Lv57xvggst6lCqNdFAmDSvoMG7M/HDtX4iUCquxQ5EGPtaPM5PL5Hmi7LCvOG8db7YaCPNJEeoI8s/WzIQw== + } + "@types/validator@13.15.10": resolution: { integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA== } + "@types/yargs-parser@21.0.3": + resolution: + { + integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + } + + "@types/yargs@17.0.35": + resolution: + { + integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg== + } + + "@ungap/structured-clone@1.3.0": + resolution: + { + integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + } + + "@unrs/resolver-binding-android-arm-eabi@1.11.1": + resolution: + { + integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw== + } + cpu: [arm] + os: [android] + + "@unrs/resolver-binding-android-arm64@1.11.1": + resolution: + { + integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g== + } + cpu: [arm64] + os: [android] + + "@unrs/resolver-binding-darwin-arm64@1.11.1": + resolution: + { + integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g== + } + cpu: [arm64] + os: [darwin] + + "@unrs/resolver-binding-darwin-x64@1.11.1": + resolution: + { + integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ== + } + cpu: [x64] + os: [darwin] + + "@unrs/resolver-binding-freebsd-x64@1.11.1": + resolution: + { + integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw== + } + cpu: [x64] + os: [freebsd] + + "@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1": + resolution: + { + integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw== + } + cpu: [arm] + os: [linux] + + "@unrs/resolver-binding-linux-arm-musleabihf@1.11.1": + resolution: + { + integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw== + } + cpu: [arm] + os: [linux] + + "@unrs/resolver-binding-linux-arm64-gnu@1.11.1": + resolution: + { + integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ== + } + cpu: [arm64] + os: [linux] + + "@unrs/resolver-binding-linux-arm64-musl@1.11.1": + resolution: + { + integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w== + } + cpu: [arm64] + os: [linux] + + "@unrs/resolver-binding-linux-ppc64-gnu@1.11.1": + resolution: + { + integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA== + } + cpu: [ppc64] + os: [linux] + + "@unrs/resolver-binding-linux-riscv64-gnu@1.11.1": + resolution: + { + integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ== + } + cpu: [riscv64] + os: [linux] + + "@unrs/resolver-binding-linux-riscv64-musl@1.11.1": + resolution: + { + integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew== + } + cpu: [riscv64] + os: [linux] + + "@unrs/resolver-binding-linux-s390x-gnu@1.11.1": + resolution: + { + integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg== + } + cpu: [s390x] + os: [linux] + + "@unrs/resolver-binding-linux-x64-gnu@1.11.1": + resolution: + { + integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w== + } + cpu: [x64] + os: [linux] + + "@unrs/resolver-binding-linux-x64-musl@1.11.1": + resolution: + { + integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA== + } + cpu: [x64] + os: [linux] + + "@unrs/resolver-binding-wasm32-wasi@1.11.1": + resolution: + { + integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ== + } + engines: { node: ">=14.0.0" } + cpu: [wasm32] + + "@unrs/resolver-binding-win32-arm64-msvc@1.11.1": + resolution: + { + integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw== + } + cpu: [arm64] + os: [win32] + + "@unrs/resolver-binding-win32-ia32-msvc@1.11.1": + resolution: + { + integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ== + } + cpu: [ia32] + os: [win32] + + "@unrs/resolver-binding-win32-x64-msvc@1.11.1": + resolution: + { + integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g== + } + cpu: [x64] + os: [win32] + accepts@2.0.0: resolution: { @@ -1563,6 +2365,13 @@ packages: integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A== } + ansi-escapes@4.3.2: + resolution: + { + integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + } + engines: { node: ">=8" } + ansi-escapes@7.3.0: resolution: { @@ -1570,6 +2379,13 @@ packages: } engines: { node: ">=18" } + ansi-regex@5.0.1: + resolution: + { + integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + } + engines: { node: ">=8" } + ansi-regex@6.2.2: resolution: { @@ -1577,6 +2393,20 @@ packages: } engines: { node: ">=12" } + ansi-styles@4.3.0: + resolution: + { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + } + engines: { node: ">=8" } + + ansi-styles@5.2.0: + resolution: + { + integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + } + engines: { node: ">=10" } + ansi-styles@6.2.3: resolution: { @@ -1603,6 +2433,24 @@ packages: integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== } + argparse@1.0.10: + resolution: + { + integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + } + + asap@2.0.6: + resolution: + { + integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + } + + asynckit@0.4.0: + resolution: + { + integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + } + aws-ssl-profiles@1.1.2: resolution: { @@ -1610,6 +2458,46 @@ packages: } engines: { node: ">= 6.0.0" } + babel-jest@30.3.0: + resolution: + { + integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + peerDependencies: + "@babel/core": ^7.11.0 || ^8.0.0-0 + + babel-plugin-istanbul@7.0.1: + resolution: + { + integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA== + } + engines: { node: ">=12" } + + babel-plugin-jest-hoist@30.3.0: + resolution: + { + integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + babel-preset-current-node-syntax@1.2.0: + resolution: + { + integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg== + } + peerDependencies: + "@babel/core": ^7.0.0 || ^8.0.0-0 + + babel-preset-jest@30.3.0: + resolution: + { + integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + peerDependencies: + "@babel/core": ^7.11.0 || ^8.0.0-beta.1 + balanced-match@1.0.2: resolution: { @@ -1623,6 +2511,14 @@ packages: } engines: { node: 18 || 20 || >=22 } + baseline-browser-mapping@2.10.14: + resolution: + { + integrity: sha512-fOVLPAsFTsQfuCkvahZkzq6nf8KvGWanlYoTh0SVA0A/PIUxQGU2AOZAoD95n2gFLVDW/jP6sbGLny95nmEuHA== + } + engines: { node: ">=6.0.0" } + hasBin: true + better-result@2.7.0: resolution: { @@ -1656,6 +2552,12 @@ packages: integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w== } + brace-expansion@2.0.3: + resolution: + { + integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA== + } + brace-expansion@5.0.5: resolution: { @@ -1670,6 +2572,27 @@ packages: } engines: { node: ">=8" } + browserslist@4.28.2: + resolution: + { + integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg== + } + engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + hasBin: true + + bs-logger@0.2.6: + resolution: + { + integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + } + engines: { node: ">= 6" } + + bser@2.1.1: + resolution: + { + integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + } + buffer-equal-constant-time@1.0.1: resolution: { @@ -1721,6 +2644,47 @@ packages: } engines: { node: ">= 0.4" } + callsites@3.1.0: + resolution: + { + integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + } + engines: { node: ">=6" } + + camelcase@5.3.1: + resolution: + { + integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + } + engines: { node: ">=6" } + + camelcase@6.3.0: + resolution: + { + integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + } + engines: { node: ">=10" } + + caniuse-lite@1.0.30001785: + resolution: + { + integrity: sha512-blhOL/WNR+Km1RI/LCVAvA73xplXA7ZbjzI4YkMK9pa6T/P3F2GxjNpEkyw5repTw9IvkyrjyHpwjnhZ5FOvYQ== + } + + chalk@4.1.2: + resolution: + { + integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + } + engines: { node: ">=10" } + + char-regex@1.0.2: + resolution: + { + integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + } + engines: { node: ">=10" } + chart.js@4.5.1: resolution: { @@ -1742,6 +2706,13 @@ packages: } engines: { node: ">= 14.16.0" } + ci-info@4.4.0: + resolution: + { + integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg== + } + engines: { node: ">=8" } + citty@0.1.6: resolution: { @@ -1754,6 +2725,12 @@ packages: integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w== } + cjs-module-lexer@2.2.0: + resolution: + { + integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ== + } + class-transformer@0.5.1: resolution: { @@ -1780,12 +2757,52 @@ packages: } engines: { node: ">=20" } + cliui@8.0.1: + resolution: + { + integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + } + engines: { node: ">=12" } + + co@4.6.0: + resolution: + { + integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + } + engines: { iojs: ">= 1.0.0", node: ">= 0.12.0" } + + collect-v8-coverage@1.0.3: + resolution: + { + integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw== + } + + color-convert@2.0.1: + resolution: + { + integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + } + engines: { node: ">=7.0.0" } + + color-name@1.1.4: + resolution: + { + integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + } + colorette@2.0.20: resolution: { integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== } + combined-stream@1.0.8: + resolution: + { + integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + } + engines: { node: ">= 0.8" } + commander@14.0.3: resolution: { @@ -1793,6 +2810,12 @@ packages: } engines: { node: ">=20" } + component-emitter@1.3.1: + resolution: + { + integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== + } + concat-map@0.0.1: resolution: { @@ -1833,6 +2856,12 @@ packages: } engines: { node: ">= 0.6" } + convert-source-map@2.0.0: + resolution: + { + integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + } + cookie-signature@1.2.2: resolution: { @@ -1847,6 +2876,12 @@ packages: } engines: { node: ">= 0.6" } + cookiejar@2.1.4: + resolution: + { + integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== + } + cors@2.8.6: resolution: { @@ -1885,6 +2920,17 @@ packages: supports-color: optional: true + dedent@1.7.2: + resolution: + { + integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA== + } + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + deep-is@0.1.4: resolution: { @@ -1898,12 +2944,26 @@ packages: } engines: { node: ">=16.0.0" } + deepmerge@4.3.1: + resolution: + { + integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + } + engines: { node: ">=0.10.0" } + defu@6.1.6: resolution: { integrity: sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug== } + delayed-stream@1.0.0: + resolution: + { + integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + } + engines: { node: ">=0.4.0" } + denque@2.1.0: resolution: { @@ -1924,6 +2984,19 @@ packages: integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA== } + detect-newline@3.1.0: + resolution: + { + integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + } + engines: { node: ">=8" } + + dezalgo@1.0.4: + resolution: + { + integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== + } + diff@4.0.4: resolution: { @@ -1965,6 +3038,12 @@ packages: integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ== } + eastasianwidth@0.2.0: + resolution: + { + integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + } + ecdsa-sig-formatter@1.0.11: resolution: { @@ -1983,12 +3062,37 @@ packages: integrity: sha512-qMLfDJscrNG8p/aw+IkT9W7fgj50Z4wG5bLBy0Txsxz8iUHjDIkOgO3SV0WZfnQbNG2VJYb0b+rDLMrhM4+Krw== } + electron-to-chromium@1.5.331: + resolution: + { + integrity: sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q== + } + + emittery@0.13.1: + resolution: + { + integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + } + engines: { node: ">=12" } + emoji-regex@10.6.0: resolution: { integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== } + emoji-regex@8.0.0: + resolution: + { + integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + } + + emoji-regex@9.2.2: + resolution: + { + integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + } + empathic@2.0.0: resolution: { @@ -2017,6 +3121,12 @@ packages: } engines: { node: ">=18" } + error-ex@1.3.4: + resolution: + { + integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== + } + es-define-property@1.0.1: resolution: { @@ -2038,12 +3148,33 @@ packages: } engines: { node: ">= 0.4" } + es-set-tostringtag@2.1.0: + resolution: + { + integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + } + engines: { node: ">= 0.4" } + + escalade@3.2.0: + resolution: + { + integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + } + engines: { node: ">=6" } + escape-html@1.0.3: resolution: { integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== } + escape-string-regexp@2.0.0: + resolution: + { + integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + } + engines: { node: ">=8" } + escape-string-regexp@4.0.0: resolution: { @@ -2092,6 +3223,14 @@ packages: } engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + esprima@4.0.1: + resolution: + { + integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + } + engines: { node: ">=4" } + hasBin: true + esquery@1.7.0: resolution: { @@ -2133,6 +3272,27 @@ packages: integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== } + execa@5.1.1: + resolution: + { + integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + } + engines: { node: ">=10" } + + exit-x@0.2.2: + resolution: + { + integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ== + } + engines: { node: ">= 0.8.0" } + + expect@30.3.0: + resolution: + { + integrity: sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + express@5.2.1: resolution: { @@ -2196,6 +3356,12 @@ packages: } hasBin: true + fb-watchman@2.0.2: + resolution: + { + integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + } + file-entry-cache@8.0.0: resolution: { @@ -2224,6 +3390,13 @@ packages: } engines: { node: ">= 18.0.0" } + find-up@4.1.0: + resolution: + { + integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + } + engines: { node: ">=8" } + find-up@5.0.0: resolution: { @@ -2251,6 +3424,20 @@ packages: } engines: { node: ">=14" } + form-data@4.0.5: + resolution: + { + integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== + } + engines: { node: ">= 6" } + + formidable@3.5.4: + resolution: + { + integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug== + } + engines: { node: ">=14.0.0" } + forwarded@0.2.0: resolution: { @@ -2291,6 +3478,20 @@ packages: integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== } + gensync@1.0.0-beta.2: + resolution: + { + integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + } + engines: { node: ">=6.9.0" } + + get-caller-file@2.0.5: + resolution: + { + integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + } + engines: { node: 6.* || 8.* || >= 10.* } + get-east-asian-width@1.5.0: resolution: { @@ -2305,6 +3506,13 @@ packages: } engines: { node: ">= 0.4" } + get-package-type@0.1.0: + resolution: + { + integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + } + engines: { node: ">=8.0.0" } + get-port-please@3.2.0: resolution: { @@ -2318,6 +3526,13 @@ packages: } engines: { node: ">= 0.4" } + get-stream@6.0.1: + resolution: + { + integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + } + engines: { node: ">=10" } + giget@2.0.0: resolution: { @@ -2339,6 +3554,14 @@ packages: } engines: { node: ">=10.13.0" } + glob@10.5.0: + resolution: + { + integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== + } + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + glob@7.2.3: resolution: { @@ -2378,6 +3601,21 @@ packages: integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg== } + handlebars@4.7.9: + resolution: + { + integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ== + } + engines: { node: ">=0.4.7" } + hasBin: true + + has-flag@4.0.0: + resolution: + { + integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + } + engines: { node: ">=8" } + has-symbols@1.1.0: resolution: { @@ -2385,6 +3623,13 @@ packages: } engines: { node: ">= 0.4" } + has-tostringtag@1.0.2: + resolution: + { + integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + } + engines: { node: ">= 0.4" } + hasown@2.0.2: resolution: { @@ -2399,6 +3644,12 @@ packages: } engines: { node: ">=16.9.0" } + html-escaper@2.0.2: + resolution: + { + integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + } + http-errors@2.0.1: resolution: { @@ -2412,6 +3663,13 @@ packages: integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA== } + human-signals@2.1.0: + resolution: + { + integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + } + engines: { node: ">=10.17.0" } + husky@9.1.7: resolution: { @@ -2440,6 +3698,14 @@ packages: } engines: { node: ">= 4" } + import-local@3.2.0: + resolution: + { + integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + } + engines: { node: ">=8" } + hasBin: true + imurmurhash@0.1.4: resolution: { @@ -2467,6 +3733,12 @@ packages: } engines: { node: ">= 0.10" } + is-arrayish@0.2.1: + resolution: + { + integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + } + is-binary-path@2.1.0: resolution: { @@ -2488,6 +3760,13 @@ packages: } engines: { node: ">=0.10.0" } + is-fullwidth-code-point@3.0.0: + resolution: + { + integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + } + engines: { node: ">=8" } + is-fullwidth-code-point@5.1.0: resolution: { @@ -2495,6 +3774,13 @@ packages: } engines: { node: ">=18" } + is-generator-fn@2.1.0: + resolution: + { + integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + } + engines: { node: ">=6" } + is-glob@4.0.3: resolution: { @@ -2521,12 +3807,54 @@ packages: integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== } + is-stream@2.0.1: + resolution: + { + integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + } + engines: { node: ">=8" } + isexe@2.0.0: resolution: { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== } + istanbul-lib-coverage@3.2.2: + resolution: + { + integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + } + engines: { node: ">=8" } + + istanbul-lib-instrument@6.0.3: + resolution: + { + integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== + } + engines: { node: ">=10" } + + istanbul-lib-report@3.0.1: + resolution: + { + integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + } + engines: { node: ">=10" } + + istanbul-lib-source-maps@5.0.6: + resolution: + { + integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== + } + engines: { node: ">=10" } + + istanbul-reports@3.2.0: + resolution: + { + integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== + } + engines: { node: ">=8" } + iterare@1.2.1: resolution: { @@ -2534,6 +3862,215 @@ packages: } engines: { node: ">=6" } + jackspeak@3.4.3: + resolution: + { + integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + } + + jest-changed-files@30.3.0: + resolution: + { + integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-circus@30.3.0: + resolution: + { + integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-cli@30.3.0: + resolution: + { + integrity: sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@30.3.0: + resolution: + { + integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + peerDependencies: + "@types/node": "*" + esbuild-register: ">=3.4.0" + ts-node: ">=9.0.0" + peerDependenciesMeta: + "@types/node": + optional: true + esbuild-register: + optional: true + ts-node: + optional: true + + jest-diff@30.3.0: + resolution: + { + integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-docblock@30.2.0: + resolution: + { + integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-each@30.3.0: + resolution: + { + integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-environment-node@30.3.0: + resolution: + { + integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-haste-map@30.3.0: + resolution: + { + integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-leak-detector@30.3.0: + resolution: + { + integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-matcher-utils@30.3.0: + resolution: + { + integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-message-util@30.3.0: + resolution: + { + integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-mock@30.3.0: + resolution: + { + integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-pnp-resolver@1.2.3: + resolution: + { + integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + } + engines: { node: ">=6" } + peerDependencies: + jest-resolve: "*" + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@30.0.1: + resolution: + { + integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-resolve-dependencies@30.3.0: + resolution: + { + integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-resolve@30.3.0: + resolution: + { + integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-runner@30.3.0: + resolution: + { + integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-runtime@30.3.0: + resolution: + { + integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-snapshot@30.3.0: + resolution: + { + integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-util@30.3.0: + resolution: + { + integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-validate@30.3.0: + resolution: + { + integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-watcher@30.3.0: + resolution: + { + integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest-worker@30.3.0: + resolution: + { + integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + + jest@30.3.0: + resolution: + { + integrity: sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + jiti@2.6.1: resolution: { @@ -2541,12 +4078,39 @@ packages: } hasBin: true + js-tokens@4.0.0: + resolution: + { + integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + } + + js-yaml@3.14.2: + resolution: + { + integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== + } + hasBin: true + + jsesc@3.1.0: + resolution: + { + integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + } + engines: { node: ">=6" } + hasBin: true + json-buffer@3.0.1: resolution: { integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== } + json-parse-even-better-errors@2.3.1: + resolution: + { + integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + } + json-schema-traverse@0.4.1: resolution: { @@ -2565,6 +4129,14 @@ packages: integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== } + json5@2.2.3: + resolution: + { + integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + } + engines: { node: ">=6" } + hasBin: true + jsonwebtoken@9.0.3: resolution: { @@ -2590,6 +4162,13 @@ packages: integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== } + leven@3.1.0: + resolution: + { + integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + } + engines: { node: ">=6" } + levn@0.4.1: resolution: { @@ -2603,6 +4182,12 @@ packages: integrity: sha512-lsmMmGXBxXIK/VMLEj0kL6MtUs1kBGj1nTCzi6zgQoG1DEwqwt2DQyHxcLykceIxAnfE3hya7NuIh6PpC6S3fA== } + lines-and-columns@1.2.4: + resolution: + { + integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + } + lint-staged@16.4.0: resolution: { @@ -2625,6 +4210,13 @@ packages: } engines: { node: ">=13.2.0" } + locate-path@5.0.0: + resolution: + { + integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + } + engines: { node: ">=8" } + locate-path@6.0.0: resolution: { @@ -2668,6 +4260,12 @@ packages: integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== } + lodash.memoize@4.1.2: + resolution: + { + integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + } + lodash.once@4.1.1: resolution: { @@ -2693,6 +4291,18 @@ packages: integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== } + lru-cache@10.4.3: + resolution: + { + integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + } + + lru-cache@5.1.1: + resolution: + { + integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + } + lru.min@1.1.4: resolution: { @@ -2700,12 +4310,25 @@ packages: } engines: { bun: ">=1.0.0", deno: ">=1.30.0", node: ">=8.0.0" } + make-dir@4.0.0: + resolution: + { + integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + } + engines: { node: ">=10" } + make-error@1.3.6: resolution: { integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== } + makeerror@1.0.12: + resolution: + { + integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + } + math-intrinsics@1.1.0: resolution: { @@ -2734,6 +4357,19 @@ packages: } engines: { node: ">=18" } + merge-stream@2.0.0: + resolution: + { + integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + } + + methods@1.1.2: + resolution: + { + integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + } + engines: { node: ">= 0.6" } + mime-db@1.52.0: resolution: { @@ -2762,6 +4398,21 @@ packages: } engines: { node: ">=18" } + mime@2.6.0: + resolution: + { + integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + } + engines: { node: ">=4.0.0" } + hasBin: true + + mimic-fn@2.1.0: + resolution: + { + integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + } + engines: { node: ">=6" } + mimic-function@5.0.1: resolution: { @@ -2782,12 +4433,26 @@ packages: integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== } + minimatch@9.0.9: + resolution: + { + integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== + } + engines: { node: ">=16 || 14 >=14.17" } + minimist@1.2.8: resolution: { integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== } + minipass@7.1.3: + resolution: + { + integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== + } + engines: { node: ">=16 || 14 >=14.17" } + mkdirp@1.0.4: resolution: { @@ -2823,6 +4488,14 @@ packages: } engines: { node: ">=8.0.0" } + napi-postinstall@0.3.4: + resolution: + { + integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ== + } + engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } + hasBin: true + natural-compare@1.4.0: resolution: { @@ -2836,12 +4509,30 @@ packages: } engines: { node: ">= 0.6" } + neo-async@2.6.2: + resolution: + { + integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + } + node-fetch-native@1.6.7: resolution: { integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q== } + node-int64@0.4.0: + resolution: + { + integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + } + + node-releases@2.0.37: + resolution: + { + integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg== + } + normalize-path@3.0.0: resolution: { @@ -2849,6 +4540,13 @@ packages: } engines: { node: ">=0.10.0" } + npm-run-path@4.0.1: + resolution: + { + integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + } + engines: { node: ">=8" } + nypm@0.6.5: resolution: { @@ -2890,6 +4588,13 @@ packages: integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== } + onetime@5.1.2: + resolution: + { + integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + } + engines: { node: ">=6" } + onetime@7.0.0: resolution: { @@ -2910,6 +4615,13 @@ packages: integrity: sha512-RUcYcRMCgRWhUE/XabRppXpUwCwaWBNHe5iPXhdvP8wwDGpGpsIf/kxX/ec3zFsOaM1Oq8lEhUqDwk6W7DHkwg== } + p-limit@2.3.0: + resolution: + { + integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + } + engines: { node: ">=6" } + p-limit@3.1.0: resolution: { @@ -2917,6 +4629,13 @@ packages: } engines: { node: ">=10" } + p-locate@4.1.0: + resolution: + { + integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + } + engines: { node: ">=8" } + p-locate@5.0.0: resolution: { @@ -2924,6 +4643,26 @@ packages: } engines: { node: ">=10" } + p-try@2.2.0: + resolution: + { + integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + } + engines: { node: ">=6" } + + package-json-from-dist@1.0.1: + resolution: + { + integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + } + + parse-json@5.2.0: + resolution: + { + integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + } + engines: { node: ">=8" } + parseurl@1.3.3: resolution: { @@ -2965,6 +4704,13 @@ packages: integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== } + path-scurry@1.11.1: + resolution: + { + integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + } + engines: { node: ">=16 || 14 >=14.18" } + path-to-regexp@8.4.2: resolution: { @@ -3003,6 +4749,20 @@ packages: } engines: { node: ">=12" } + pirates@4.0.7: + resolution: + { + integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== + } + engines: { node: ">= 6" } + + pkg-dir@4.2.0: + resolution: + { + integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + } + engines: { node: ">=8" } + pkg-types@2.3.0: resolution: { @@ -3031,6 +4791,13 @@ packages: engines: { node: ">=14" } hasBin: true + pretty-format@30.3.0: + resolution: + { + integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ== + } + engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + prisma@7.6.0: resolution: { @@ -3073,6 +4840,12 @@ packages: integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== } + pure-rand@7.0.1: + resolution: + { + integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ== + } + qs@6.15.0: resolution: { @@ -3108,6 +4881,12 @@ packages: peerDependencies: react: ^19.2.4 + react-is@18.3.1: + resolution: + { + integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + } + react@19.2.4: resolution: { @@ -3148,6 +4927,13 @@ packages: integrity: sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ== } + require-directory@2.1.1: + resolution: + { + integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + } + engines: { node: ">=0.10.0" } + require-from-string@2.0.2: resolution: { @@ -3155,6 +4941,20 @@ packages: } engines: { node: ">=0.10.0" } + resolve-cwd@3.0.0: + resolution: + { + integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + } + engines: { node: ">=8" } + + resolve-from@5.0.0: + resolution: + { + integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + } + engines: { node: ">=8" } + resolve@1.22.11: resolution: { @@ -3222,6 +5022,13 @@ packages: integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== } + semver@6.3.1: + resolution: + { + integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + } + hasBin: true + semver@7.7.4: resolution: { @@ -3317,6 +5124,13 @@ packages: integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== } + slash@3.0.0: + resolution: + { + integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + } + engines: { node: ">=8" } + slice-ansi@7.1.2: resolution: { @@ -3331,6 +5145,12 @@ packages: } engines: { node: ">=20" } + source-map-support@0.5.13: + resolution: + { + integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + } + source-map-support@0.5.21: resolution: { @@ -3344,6 +5164,12 @@ packages: } engines: { node: ">=0.10.0" } + sprintf-js@1.0.3: + resolution: + { + integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + } + sqlstring@2.3.3: resolution: { @@ -3351,6 +5177,13 @@ packages: } engines: { node: ">= 0.6" } + stack-utils@2.0.6: + resolution: + { + integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + } + engines: { node: ">=10" } + statuses@2.0.2: resolution: { @@ -3378,6 +5211,27 @@ packages: } engines: { node: ">=0.6.19" } + string-length@4.0.2: + resolution: + { + integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + } + engines: { node: ">=10" } + + string-width@4.2.3: + resolution: + { + integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + } + engines: { node: ">=8" } + + string-width@5.1.2: + resolution: + { + integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + } + engines: { node: ">=12" } + string-width@7.2.0: resolution: { @@ -3398,6 +5252,13 @@ packages: integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== } + strip-ansi@6.0.1: + resolution: + { + integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + } + engines: { node: ">=8" } + strip-ansi@7.2.0: resolution: { @@ -3412,6 +5273,20 @@ packages: } engines: { node: ">=4" } + strip-bom@4.0.0: + resolution: + { + integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + } + engines: { node: ">=8" } + + strip-final-newline@2.0.0: + resolution: + { + integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + } + engines: { node: ">=6" } + strip-json-comments@2.0.1: resolution: { @@ -3419,6 +5294,13 @@ packages: } engines: { node: ">=0.10.0" } + strip-json-comments@3.1.1: + resolution: + { + integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + } + engines: { node: ">=8" } + strnum@2.2.2: resolution: { @@ -3432,6 +5314,34 @@ packages: } engines: { node: ">=18" } + superagent@10.3.0: + resolution: + { + integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ== + } + engines: { node: ">=14.18.0" } + + supertest@7.2.2: + resolution: + { + integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA== + } + engines: { node: ">=14.18.0" } + + supports-color@7.2.0: + resolution: + { + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + } + engines: { node: ">=8" } + + supports-color@8.1.1: + resolution: + { + integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + } + engines: { node: ">=10" } + supports-preserve-symlinks-flag@1.0.0: resolution: { @@ -3439,6 +5349,20 @@ packages: } engines: { node: ">= 0.4" } + synckit@0.11.12: + resolution: + { + integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ== + } + engines: { node: ^14.18.0 || >=16.0.0 } + + test-exclude@6.0.0: + resolution: + { + integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + } + engines: { node: ">=8" } + thirty-two@1.0.2: resolution: { @@ -3453,6 +5377,12 @@ packages: } engines: { node: ">=18" } + tmpl@1.0.5: + resolution: + { + integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + } + to-regex-range@5.0.1: resolution: { @@ -3481,6 +5411,36 @@ packages: } hasBin: true + ts-jest@29.4.9: + resolution: + { + integrity: sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ== + } + engines: { node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0 } + hasBin: true + peerDependencies: + "@babel/core": ">=7.0.0-beta.0 <8" + "@jest/transform": ^29.0.0 || ^30.0.0 + "@jest/types": ^29.0.0 || ^30.0.0 + babel-jest: ^29.0.0 || ^30.0.0 + esbuild: "*" + jest: ^29.0.0 || ^30.0.0 + jest-util: ^29.0.0 || ^30.0.0 + typescript: ">=4.3 <7" + peerDependenciesMeta: + "@babel/core": + optional: true + "@jest/transform": + optional: true + "@jest/types": + optional: true + babel-jest: + optional: true + esbuild: + optional: true + jest-util: + optional: true + ts-node-dev@2.0.0: resolution: { @@ -3538,6 +5498,27 @@ packages: } engines: { node: ">= 0.8.0" } + type-detect@4.0.8: + resolution: + { + integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + } + engines: { node: ">=4" } + + type-fest@0.21.3: + resolution: + { + integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + } + engines: { node: ">=10" } + + type-fest@4.41.0: + resolution: + { + integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + } + engines: { node: ">=16" } + type-is@1.6.18: resolution: { @@ -3566,6 +5547,14 @@ packages: engines: { node: ">=14.17" } hasBin: true + uglify-js@3.19.3: + resolution: + { + integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== + } + engines: { node: ">=0.8.0" } + hasBin: true + uid@2.0.2: resolution: { @@ -3593,6 +5582,21 @@ packages: } engines: { node: ">= 0.8" } + unrs-resolver@1.11.1: + resolution: + { + integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg== + } + + update-browserslist-db@1.2.3: + resolution: + { + integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== + } + hasBin: true + peerDependencies: + browserslist: ">= 4.21.0" + uri-js@4.4.1: resolution: { @@ -3611,6 +5615,13 @@ packages: integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== } + v8-to-istanbul@9.3.0: + resolution: + { + integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== + } + engines: { node: ">=10.12.0" } + valibot@1.2.0: resolution: { @@ -3636,6 +5647,12 @@ packages: } engines: { node: ">= 0.8" } + walker@1.0.8: + resolution: + { + integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + } + which@2.0.2: resolution: { @@ -3651,6 +5668,26 @@ packages: } engines: { node: ">=0.10.0" } + wordwrap@1.0.0: + resolution: + { + integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + } + + wrap-ansi@7.0.0: + resolution: + { + integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + } + engines: { node: ">=10" } + + wrap-ansi@8.1.0: + resolution: + { + integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + } + engines: { node: ">=12" } + wrap-ansi@9.0.2: resolution: { @@ -3664,6 +5701,13 @@ packages: integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== } + write-file-atomic@5.0.1: + resolution: + { + integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== + } + engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + xtend@4.0.2: resolution: { @@ -3671,6 +5715,19 @@ packages: } engines: { node: ">=0.4" } + y18n@5.0.8: + resolution: + { + integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + } + engines: { node: ">=10" } + + yallist@3.1.1: + resolution: + { + integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + } + yaml@2.8.3: resolution: { @@ -3679,6 +5736,20 @@ packages: engines: { node: ">= 14.6" } hasBin: true + yargs-parser@21.1.1: + resolution: + { + integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + } + engines: { node: ">=12" } + + yargs@17.7.2: + resolution: + { + integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + } + engines: { node: ">=12" } + yn@3.1.1: resolution: { @@ -4162,6 +6233,195 @@ snapshots: "@aws/lambda-invoke-store@0.2.4": {} + "@babel/code-frame@7.29.0": + dependencies: + "@babel/helper-validator-identifier": 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + "@babel/compat-data@7.29.0": {} + + "@babel/core@7.29.0": + dependencies: + "@babel/code-frame": 7.29.0 + "@babel/generator": 7.29.1 + "@babel/helper-compilation-targets": 7.28.6 + "@babel/helper-module-transforms": 7.28.6(@babel/core@7.29.0) + "@babel/helpers": 7.29.2 + "@babel/parser": 7.29.2 + "@babel/template": 7.28.6 + "@babel/traverse": 7.29.0 + "@babel/types": 7.29.0 + "@jridgewell/remapping": 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + "@babel/generator@7.29.1": + dependencies: + "@babel/parser": 7.29.2 + "@babel/types": 7.29.0 + "@jridgewell/gen-mapping": 0.3.13 + "@jridgewell/trace-mapping": 0.3.31 + jsesc: 3.1.0 + + "@babel/helper-compilation-targets@7.28.6": + dependencies: + "@babel/compat-data": 7.29.0 + "@babel/helper-validator-option": 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + "@babel/helper-globals@7.28.0": {} + + "@babel/helper-module-imports@7.28.6": + dependencies: + "@babel/traverse": 7.29.0 + "@babel/types": 7.29.0 + transitivePeerDependencies: + - supports-color + + "@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-module-imports": 7.28.6 + "@babel/helper-validator-identifier": 7.28.5 + "@babel/traverse": 7.29.0 + transitivePeerDependencies: + - supports-color + + "@babel/helper-plugin-utils@7.28.6": {} + + "@babel/helper-string-parser@7.27.1": {} + + "@babel/helper-validator-identifier@7.28.5": {} + + "@babel/helper-validator-option@7.27.1": {} + + "@babel/helpers@7.29.2": + dependencies: + "@babel/template": 7.28.6 + "@babel/types": 7.29.0 + + "@babel/parser@7.29.2": + dependencies: + "@babel/types": 7.29.0 + + "@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 + + "@babel/template@7.28.6": + dependencies: + "@babel/code-frame": 7.29.0 + "@babel/parser": 7.29.2 + "@babel/types": 7.29.0 + + "@babel/traverse@7.29.0": + dependencies: + "@babel/code-frame": 7.29.0 + "@babel/generator": 7.29.1 + "@babel/helper-globals": 7.28.0 + "@babel/parser": 7.29.2 + "@babel/template": 7.28.6 + "@babel/types": 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + "@babel/types@7.29.0": + dependencies: + "@babel/helper-string-parser": 7.27.1 + "@babel/helper-validator-identifier": 7.28.5 + + "@bcoe/v8-coverage@0.2.3": {} + "@borewit/text-codec@0.2.2": {} "@clack/core@0.5.0": @@ -4189,6 +6449,22 @@ snapshots: "@electric-sql/pglite@0.4.1": {} + "@emnapi/core@1.9.2": + dependencies: + "@emnapi/wasi-threads": 1.2.1 + tslib: 2.8.1 + optional: true + + "@emnapi/runtime@1.9.2": + dependencies: + tslib: 2.8.1 + optional: true + + "@emnapi/wasi-threads@1.2.1": + dependencies: + tslib: 2.8.1 + optional: true + "@eslint-community/eslint-utils@4.9.1(eslint@10.2.0(jiti@2.6.1))": dependencies: eslint: 10.2.0(jiti@2.6.1) @@ -4238,10 +6514,221 @@ snapshots: "@humanwhocodes/retry@0.4.3": {} + "@isaacs/cliui@8.0.2": + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + "@istanbuljs/load-nyc-config@1.1.0": + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.2 + resolve-from: 5.0.0 + + "@istanbuljs/schema@0.1.3": {} + + "@jest/console@30.3.0": + dependencies: + "@jest/types": 30.3.0 + "@types/node": 25.5.2 + chalk: 4.1.2 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + slash: 3.0.0 + + "@jest/core@30.3.0(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3))": + dependencies: + "@jest/console": 30.3.0 + "@jest/pattern": 30.0.1 + "@jest/reporters": 30.3.0 + "@jest/test-result": 30.3.0 + "@jest/transform": 30.3.0 + "@jest/types": 30.3.0 + "@types/node": 25.5.2 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.3.0 + jest-config: 30.3.0(@types/node@25.5.2)(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)) + jest-haste-map: 30.3.0 + jest-message-util: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-resolve-dependencies: 30.3.0 + jest-runner: 30.3.0 + jest-runtime: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + jest-watcher: 30.3.0 + pretty-format: 30.3.0 + slash: 3.0.0 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + + "@jest/diff-sequences@30.3.0": {} + + "@jest/environment@30.3.0": + dependencies: + "@jest/fake-timers": 30.3.0 + "@jest/types": 30.3.0 + "@types/node": 25.5.2 + jest-mock: 30.3.0 + + "@jest/expect-utils@30.3.0": + dependencies: + "@jest/get-type": 30.1.0 + + "@jest/expect@30.3.0": + dependencies: + expect: 30.3.0 + jest-snapshot: 30.3.0 + transitivePeerDependencies: + - supports-color + + "@jest/fake-timers@30.3.0": + dependencies: + "@jest/types": 30.3.0 + "@sinonjs/fake-timers": 15.3.0 + "@types/node": 25.5.2 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-util: 30.3.0 + + "@jest/get-type@30.1.0": {} + + "@jest/globals@30.3.0": + dependencies: + "@jest/environment": 30.3.0 + "@jest/expect": 30.3.0 + "@jest/types": 30.3.0 + jest-mock: 30.3.0 + transitivePeerDependencies: + - supports-color + + "@jest/pattern@30.0.1": + dependencies: + "@types/node": 25.5.2 + jest-regex-util: 30.0.1 + + "@jest/reporters@30.3.0": + dependencies: + "@bcoe/v8-coverage": 0.2.3 + "@jest/console": 30.3.0 + "@jest/test-result": 30.3.0 + "@jest/transform": 30.3.0 + "@jest/types": 30.3.0 + "@jridgewell/trace-mapping": 0.3.31 + "@types/node": 25.5.2 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3 + exit-x: 0.2.2 + glob: 10.5.0 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + jest-worker: 30.3.0 + slash: 3.0.0 + string-length: 4.0.2 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + "@jest/schemas@30.0.5": + dependencies: + "@sinclair/typebox": 0.34.49 + + "@jest/snapshot-utils@30.3.0": + dependencies: + "@jest/types": 30.3.0 + chalk: 4.1.2 + graceful-fs: 4.2.11 + natural-compare: 1.4.0 + + "@jest/source-map@30.0.1": + dependencies: + "@jridgewell/trace-mapping": 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + "@jest/test-result@30.3.0": + dependencies: + "@jest/console": 30.3.0 + "@jest/types": 30.3.0 + "@types/istanbul-lib-coverage": 2.0.6 + collect-v8-coverage: 1.0.3 + + "@jest/test-sequencer@30.3.0": + dependencies: + "@jest/test-result": 30.3.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + slash: 3.0.0 + + "@jest/transform@30.3.0": + dependencies: + "@babel/core": 7.29.0 + "@jest/types": 30.3.0 + "@jridgewell/trace-mapping": 0.3.31 + babel-plugin-istanbul: 7.0.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-regex-util: 30.0.1 + jest-util: 30.3.0 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + + "@jest/types@30.3.0": + dependencies: + "@jest/pattern": 30.0.1 + "@jest/schemas": 30.0.5 + "@types/istanbul-lib-coverage": 2.0.6 + "@types/istanbul-reports": 3.0.4 + "@types/node": 25.5.2 + "@types/yargs": 17.0.35 + chalk: 4.1.2 + + "@jridgewell/gen-mapping@0.3.13": + dependencies: + "@jridgewell/sourcemap-codec": 1.5.5 + "@jridgewell/trace-mapping": 0.3.31 + + "@jridgewell/remapping@2.3.5": + dependencies: + "@jridgewell/gen-mapping": 0.3.13 + "@jridgewell/trace-mapping": 0.3.31 + "@jridgewell/resolve-uri@3.1.2": {} "@jridgewell/sourcemap-codec@1.5.5": {} + "@jridgewell/trace-mapping@0.3.31": + dependencies: + "@jridgewell/resolve-uri": 3.1.2 + "@jridgewell/sourcemap-codec": 1.5.5 + "@jridgewell/trace-mapping@0.3.9": dependencies: "@jridgewell/resolve-uri": 3.1.2 @@ -4251,6 +6738,13 @@ snapshots: "@lukeed/csprng@1.1.0": {} + "@napi-rs/wasm-runtime@0.2.12": + dependencies: + "@emnapi/core": 1.9.2 + "@emnapi/runtime": 1.9.2 + "@tybys/wasm-util": 0.10.1 + optional: true + "@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)": dependencies: file-type: 21.3.4 @@ -4306,6 +6800,16 @@ snapshots: transitivePeerDependencies: - supports-color + "@nestjs/testing@11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(@nestjs/platform-express@11.1.18)": + dependencies: + "@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + "@nestjs/core": 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) + tslib: 2.8.1 + optionalDependencies: + "@nestjs/platform-express": 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18) + + "@noble/hashes@1.8.0": {} + "@noble/hashes@2.0.1": {} "@nuxt/opencollective@0.4.1": @@ -4356,6 +6860,15 @@ snapshots: dependencies: "@otplib/core": 13.4.0 + "@paralleldrive/cuid2@2.3.1": + dependencies: + "@noble/hashes": 1.8.0 + + "@pkgjs/parseargs@0.11.0": + optional: true + + "@pkgr/core@0.2.9": {} + "@prisma/client-runtime-utils@7.6.0": {} "@prisma/client@7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)": @@ -4498,6 +7011,16 @@ snapshots: "@scure/base@2.0.0": {} + "@sinclair/typebox@0.34.49": {} + + "@sinonjs/commons@3.0.1": + dependencies: + type-detect: 4.0.8 + + "@sinonjs/fake-timers@15.3.0": + dependencies: + "@sinonjs/commons": 3.0.1 + "@smithy/chunked-blob-reader-native@4.2.3": dependencies: "@smithy/util-base64": 4.3.2 @@ -4867,6 +7390,32 @@ snapshots: "@turbo/windows-arm64@2.9.3": optional: true + "@tybys/wasm-util@0.10.1": + dependencies: + tslib: 2.8.1 + optional: true + + "@types/babel__core@7.20.5": + dependencies: + "@babel/parser": 7.29.2 + "@babel/types": 7.29.0 + "@types/babel__generator": 7.27.0 + "@types/babel__template": 7.4.4 + "@types/babel__traverse": 7.28.0 + + "@types/babel__generator@7.27.0": + dependencies: + "@babel/types": 7.29.0 + + "@types/babel__template@7.4.4": + dependencies: + "@babel/parser": 7.29.2 + "@babel/types": 7.29.0 + + "@types/babel__traverse@7.28.0": + dependencies: + "@babel/types": 7.29.0 + "@types/body-parser@1.19.6": dependencies: "@types/connect": 3.4.38 @@ -4876,6 +7425,8 @@ snapshots: dependencies: "@types/node": 25.5.2 + "@types/cookiejar@2.1.5": {} + "@types/esrecurse@4.3.1": {} "@types/estree@1.0.8": {} @@ -4895,6 +7446,21 @@ snapshots: "@types/http-errors@2.0.5": {} + "@types/istanbul-lib-coverage@2.0.6": {} + + "@types/istanbul-lib-report@3.0.3": + dependencies: + "@types/istanbul-lib-coverage": 2.0.6 + + "@types/istanbul-reports@3.0.4": + dependencies: + "@types/istanbul-lib-report": 3.0.3 + + "@types/jest@30.0.0": + dependencies: + expect: 30.3.0 + pretty-format: 30.3.0 + "@types/json-schema@7.0.15": {} "@types/jsonwebtoken@9.0.10": @@ -4902,6 +7468,8 @@ snapshots: "@types/ms": 2.1.0 "@types/node": 25.5.2 + "@types/methods@1.1.4": {} + "@types/ms@2.1.0": {} "@types/node@25.5.2": @@ -4945,12 +7513,93 @@ snapshots: "@types/http-errors": 2.0.5 "@types/node": 25.5.2 + "@types/stack-utils@2.0.3": {} + "@types/strip-bom@3.0.0": {} "@types/strip-json-comments@0.0.30": {} + "@types/superagent@8.1.9": + dependencies: + "@types/cookiejar": 2.1.5 + "@types/methods": 1.1.4 + "@types/node": 25.5.2 + form-data: 4.0.5 + + "@types/supertest@7.2.0": + dependencies: + "@types/methods": 1.1.4 + "@types/superagent": 8.1.9 + "@types/validator@13.15.10": {} + "@types/yargs-parser@21.0.3": {} + + "@types/yargs@17.0.35": + dependencies: + "@types/yargs-parser": 21.0.3 + + "@ungap/structured-clone@1.3.0": {} + + "@unrs/resolver-binding-android-arm-eabi@1.11.1": + optional: true + + "@unrs/resolver-binding-android-arm64@1.11.1": + optional: true + + "@unrs/resolver-binding-darwin-arm64@1.11.1": + optional: true + + "@unrs/resolver-binding-darwin-x64@1.11.1": + optional: true + + "@unrs/resolver-binding-freebsd-x64@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-arm-musleabihf@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-arm64-gnu@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-arm64-musl@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-ppc64-gnu@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-riscv64-gnu@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-riscv64-musl@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-s390x-gnu@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-x64-gnu@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-x64-musl@1.11.1": + optional: true + + "@unrs/resolver-binding-wasm32-wasi@1.11.1": + dependencies: + "@napi-rs/wasm-runtime": 0.2.12 + optional: true + + "@unrs/resolver-binding-win32-arm64-msvc@1.11.1": + optional: true + + "@unrs/resolver-binding-win32-ia32-msvc@1.11.1": + optional: true + + "@unrs/resolver-binding-win32-x64-msvc@1.11.1": + optional: true + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -4980,12 +7629,24 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + ansi-escapes@7.3.0: dependencies: environment: 1.1.0 + ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} anymatch@3.1.3: @@ -4997,12 +7658,74 @@ snapshots: arg@4.1.3: {} + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + asap@2.0.6: {} + + asynckit@0.4.0: {} + aws-ssl-profiles@1.1.2: {} + babel-jest@30.3.0(@babel/core@7.29.0): + dependencies: + "@babel/core": 7.29.0 + "@jest/transform": 30.3.0 + "@types/babel__core": 7.20.5 + babel-plugin-istanbul: 7.0.1 + babel-preset-jest: 30.3.0(@babel/core@7.29.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@7.0.1: + dependencies: + "@babel/helper-plugin-utils": 7.28.6 + "@istanbuljs/load-nyc-config": 1.1.0 + "@istanbuljs/schema": 0.1.3 + istanbul-lib-instrument: 6.0.3 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@30.3.0: + dependencies: + "@types/babel__core": 7.20.5 + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): + dependencies: + "@babel/core": 7.29.0 + "@babel/plugin-syntax-async-generators": 7.8.4(@babel/core@7.29.0) + "@babel/plugin-syntax-bigint": 7.8.3(@babel/core@7.29.0) + "@babel/plugin-syntax-class-properties": 7.12.13(@babel/core@7.29.0) + "@babel/plugin-syntax-class-static-block": 7.14.5(@babel/core@7.29.0) + "@babel/plugin-syntax-import-attributes": 7.28.6(@babel/core@7.29.0) + "@babel/plugin-syntax-import-meta": 7.10.4(@babel/core@7.29.0) + "@babel/plugin-syntax-json-strings": 7.8.3(@babel/core@7.29.0) + "@babel/plugin-syntax-logical-assignment-operators": 7.10.4(@babel/core@7.29.0) + "@babel/plugin-syntax-nullish-coalescing-operator": 7.8.3(@babel/core@7.29.0) + "@babel/plugin-syntax-numeric-separator": 7.10.4(@babel/core@7.29.0) + "@babel/plugin-syntax-object-rest-spread": 7.8.3(@babel/core@7.29.0) + "@babel/plugin-syntax-optional-catch-binding": 7.8.3(@babel/core@7.29.0) + "@babel/plugin-syntax-optional-chaining": 7.8.3(@babel/core@7.29.0) + "@babel/plugin-syntax-private-property-in-object": 7.14.5(@babel/core@7.29.0) + "@babel/plugin-syntax-top-level-await": 7.14.5(@babel/core@7.29.0) + + babel-preset-jest@30.3.0(@babel/core@7.29.0): + dependencies: + "@babel/core": 7.29.0 + babel-plugin-jest-hoist: 30.3.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + balanced-match@1.0.2: {} balanced-match@4.0.4: {} + baseline-browser-mapping@2.10.14: {} + better-result@2.7.0: dependencies: "@clack/prompts": 0.11.0 @@ -5030,6 +7753,10 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@2.0.3: + dependencies: + balanced-match: 1.0.2 + brace-expansion@5.0.5: dependencies: balanced-match: 4.0.4 @@ -5038,6 +7765,22 @@ snapshots: dependencies: fill-range: 7.1.1 + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.14 + caniuse-lite: 1.0.30001785 + electron-to-chromium: 1.5.331 + node-releases: 2.0.37 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + buffer-equal-constant-time@1.0.1: {} buffer-from@1.1.2: {} @@ -5073,6 +7816,21 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + callsites@3.1.0: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001785: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + chart.js@4.5.1: dependencies: "@kurkle/color": 0.3.4 @@ -5093,12 +7851,16 @@ snapshots: dependencies: readdirp: 4.1.2 + ci-info@4.4.0: {} + citty@0.1.6: dependencies: consola: 3.4.2 citty@0.2.2: {} + cjs-module-lexer@2.2.0: {} + class-transformer@0.5.1: {} class-validator@0.15.1: @@ -5116,10 +7878,32 @@ snapshots: slice-ansi: 8.0.0 string-width: 8.2.0 + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + co@4.6.0: {} + + collect-v8-coverage@1.0.3: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + colorette@2.0.20: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + commander@14.0.3: {} + component-emitter@1.3.1: {} + concat-map@0.0.1: {} concat-stream@2.0.0: @@ -5137,10 +7921,14 @@ snapshots: content-type@1.0.5: {} + convert-source-map@2.0.0: {} + cookie-signature@1.2.2: {} cookie@0.7.2: {} + cookiejar@2.1.4: {} + cors@2.8.6: dependencies: object-assign: 4.1.1 @@ -5160,18 +7948,31 @@ snapshots: dependencies: ms: 2.1.3 + dedent@1.7.2: {} + deep-is@0.1.4: {} deepmerge-ts@7.1.5: {} + deepmerge@4.3.1: {} + defu@6.1.6: {} + delayed-stream@1.0.0: {} + denque@2.1.0: {} depd@2.0.0: {} destr@2.0.5: {} + detect-newline@3.1.0: {} + + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + diff@4.0.4: {} dotenv-expand@12.0.3: @@ -5192,6 +7993,8 @@ snapshots: dependencies: xtend: 4.0.2 + eastasianwidth@0.2.0: {} + ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer: 5.2.1 @@ -5203,8 +8006,16 @@ snapshots: "@standard-schema/spec": 1.1.0 fast-check: 3.23.2 + electron-to-chromium@1.5.331: {} + + emittery@0.13.1: {} + emoji-regex@10.6.0: {} + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + empathic@2.0.0: {} encodeurl@2.0.0: {} @@ -5213,6 +8024,10 @@ snapshots: environment@1.1.0: {} + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -5221,8 +8036,19 @@ snapshots: dependencies: es-errors: 1.3.0 + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@2.0.0: {} + escape-string-regexp@4.0.0: {} eslint-scope@9.1.2: @@ -5279,6 +8105,8 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 5.0.1 + esprima@4.0.1: {} + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -5295,6 +8123,29 @@ snapshots: eventemitter3@5.0.4: {} + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit-x@0.2.2: {} + + expect@30.3.0: + dependencies: + "@jest/expect-utils": 30.3.0 + "@jest/get-type": 30.1.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-util: 30.3.0 + express@5.2.1: dependencies: accepts: 2.0.0 @@ -5354,6 +8205,10 @@ snapshots: path-expression-matcher: 1.2.1 strnum: 2.2.2 + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -5382,6 +8237,11 @@ snapshots: transitivePeerDependencies: - supports-color + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -5399,6 +8259,20 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formidable@3.5.4: + dependencies: + "@paralleldrive/cuid2": 2.3.1 + dezalgo: 1.0.4 + once: 1.4.0 + forwarded@0.2.0: {} fresh@2.0.0: {} @@ -5414,6 +8288,10 @@ snapshots: dependencies: is-property: 1.0.2 + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + get-east-asian-width@1.5.0: {} get-intrinsic@1.3.0: @@ -5429,6 +8307,8 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 + get-package-type@0.1.0: {} + get-port-please@3.2.0: {} get-proto@1.0.1: @@ -5436,6 +8316,8 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-stream@6.0.1: {} + giget@2.0.0: dependencies: citty: 0.1.6 @@ -5453,6 +8335,15 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -5472,14 +8363,31 @@ snapshots: graphmatch@1.1.1: {} + handlebars@4.7.9: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + has-flag@4.0.0: {} + has-symbols@1.1.0: {} + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.2: dependencies: function-bind: 1.1.2 hono@4.12.10: {} + html-escaper@2.0.2: {} + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -5490,6 +8398,8 @@ snapshots: http-status-codes@2.3.0: {} + human-signals@2.1.0: {} + husky@9.1.7: {} iconv-lite@0.7.2: @@ -5500,6 +8410,11 @@ snapshots: ignore@5.3.2: {} + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + imurmurhash@0.1.4: {} inflight@1.0.6: @@ -5511,6 +8426,8 @@ snapshots: ipaddr.js@1.9.1: {} + is-arrayish@0.2.1: {} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -5521,10 +8438,14 @@ snapshots: is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} + is-fullwidth-code-point@5.1.0: dependencies: get-east-asian-width: 1.5.0 + is-generator-fn@2.1.0: {} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -5535,20 +8456,383 @@ snapshots: is-property@1.0.2: {} + is-stream@2.0.1: {} + isexe@2.0.0: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@6.0.3: + dependencies: + "@babel/core": 7.29.0 + "@babel/parser": 7.29.2 + "@istanbuljs/schema": 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + "@jridgewell/trace-mapping": 0.3.31 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + iterare@1.2.1: {} + jackspeak@3.4.3: + dependencies: + "@isaacs/cliui": 8.0.2 + optionalDependencies: + "@pkgjs/parseargs": 0.11.0 + + jest-changed-files@30.3.0: + dependencies: + execa: 5.1.1 + jest-util: 30.3.0 + p-limit: 3.1.0 + + jest-circus@30.3.0: + dependencies: + "@jest/environment": 30.3.0 + "@jest/expect": 30.3.0 + "@jest/test-result": 30.3.0 + "@jest/types": 30.3.0 + "@types/node": 25.5.2 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.2 + is-generator-fn: 2.1.0 + jest-each: 30.3.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-runtime: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + p-limit: 3.1.0 + pretty-format: 30.3.0 + pure-rand: 7.0.1 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@30.3.0(@types/node@25.5.2)(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)): + dependencies: + "@jest/core": 30.3.0(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)) + "@jest/test-result": 30.3.0 + "@jest/types": 30.3.0 + chalk: 4.1.2 + exit-x: 0.2.2 + import-local: 3.2.0 + jest-config: 30.3.0(@types/node@25.5.2)(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)) + jest-util: 30.3.0 + jest-validate: 30.3.0 + yargs: 17.7.2 + transitivePeerDependencies: + - "@types/node" + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + + jest-config@30.3.0(@types/node@25.5.2)(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)): + dependencies: + "@babel/core": 7.29.0 + "@jest/get-type": 30.1.0 + "@jest/pattern": 30.0.1 + "@jest/test-sequencer": 30.3.0 + "@jest/types": 30.3.0 + babel-jest: 30.3.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.3.0 + jest-docblock: 30.2.0 + jest-environment-node: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-runner: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + parse-json: 5.2.0 + pretty-format: 30.3.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + "@types/node": 25.5.2 + ts-node: 10.9.2(@types/node@25.5.2)(typescript@5.9.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@30.3.0: + dependencies: + "@jest/diff-sequences": 30.3.0 + "@jest/get-type": 30.1.0 + chalk: 4.1.2 + pretty-format: 30.3.0 + + jest-docblock@30.2.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@30.3.0: + dependencies: + "@jest/get-type": 30.1.0 + "@jest/types": 30.3.0 + chalk: 4.1.2 + jest-util: 30.3.0 + pretty-format: 30.3.0 + + jest-environment-node@30.3.0: + dependencies: + "@jest/environment": 30.3.0 + "@jest/fake-timers": 30.3.0 + "@jest/types": 30.3.0 + "@types/node": 25.5.2 + jest-mock: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + + jest-haste-map@30.3.0: + dependencies: + "@jest/types": 30.3.0 + "@types/node": 25.5.2 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 30.0.1 + jest-util: 30.3.0 + jest-worker: 30.3.0 + picomatch: 4.0.4 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@30.3.0: + dependencies: + "@jest/get-type": 30.1.0 + pretty-format: 30.3.0 + + jest-matcher-utils@30.3.0: + dependencies: + "@jest/get-type": 30.1.0 + chalk: 4.1.2 + jest-diff: 30.3.0 + pretty-format: 30.3.0 + + jest-message-util@30.3.0: + dependencies: + "@babel/code-frame": 7.29.0 + "@jest/types": 30.3.0 + "@types/stack-utils": 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + picomatch: 4.0.4 + pretty-format: 30.3.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@30.3.0: + dependencies: + "@jest/types": 30.3.0 + "@types/node": 25.5.2 + jest-util: 30.3.0 + + jest-pnp-resolver@1.2.3(jest-resolve@30.3.0): + optionalDependencies: + jest-resolve: 30.3.0 + + jest-regex-util@30.0.1: {} + + jest-resolve-dependencies@30.3.0: + dependencies: + jest-regex-util: 30.0.1 + jest-snapshot: 30.3.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@30.3.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-pnp-resolver: 1.2.3(jest-resolve@30.3.0) + jest-util: 30.3.0 + jest-validate: 30.3.0 + slash: 3.0.0 + unrs-resolver: 1.11.1 + + jest-runner@30.3.0: + dependencies: + "@jest/console": 30.3.0 + "@jest/environment": 30.3.0 + "@jest/test-result": 30.3.0 + "@jest/transform": 30.3.0 + "@jest/types": 30.3.0 + "@types/node": 25.5.2 + chalk: 4.1.2 + emittery: 0.13.1 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-docblock: 30.2.0 + jest-environment-node: 30.3.0 + jest-haste-map: 30.3.0 + jest-leak-detector: 30.3.0 + jest-message-util: 30.3.0 + jest-resolve: 30.3.0 + jest-runtime: 30.3.0 + jest-util: 30.3.0 + jest-watcher: 30.3.0 + jest-worker: 30.3.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@30.3.0: + dependencies: + "@jest/environment": 30.3.0 + "@jest/fake-timers": 30.3.0 + "@jest/globals": 30.3.0 + "@jest/source-map": 30.0.1 + "@jest/test-result": 30.3.0 + "@jest/transform": 30.3.0 + "@jest/types": 30.3.0 + "@types/node": 25.5.2 + chalk: 4.1.2 + cjs-module-lexer: 2.2.0 + collect-v8-coverage: 1.0.3 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@30.3.0: + dependencies: + "@babel/core": 7.29.0 + "@babel/generator": 7.29.1 + "@babel/plugin-syntax-jsx": 7.28.6(@babel/core@7.29.0) + "@babel/plugin-syntax-typescript": 7.28.6(@babel/core@7.29.0) + "@babel/types": 7.29.0 + "@jest/expect-utils": 30.3.0 + "@jest/get-type": 30.1.0 + "@jest/snapshot-utils": 30.3.0 + "@jest/transform": 30.3.0 + "@jest/types": 30.3.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + chalk: 4.1.2 + expect: 30.3.0 + graceful-fs: 4.2.11 + jest-diff: 30.3.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + pretty-format: 30.3.0 + semver: 7.7.4 + synckit: 0.11.12 + transitivePeerDependencies: + - supports-color + + jest-util@30.3.0: + dependencies: + "@jest/types": 30.3.0 + "@types/node": 25.5.2 + chalk: 4.1.2 + ci-info: 4.4.0 + graceful-fs: 4.2.11 + picomatch: 4.0.4 + + jest-validate@30.3.0: + dependencies: + "@jest/get-type": 30.1.0 + "@jest/types": 30.3.0 + camelcase: 6.3.0 + chalk: 4.1.2 + leven: 3.1.0 + pretty-format: 30.3.0 + + jest-watcher@30.3.0: + dependencies: + "@jest/test-result": 30.3.0 + "@jest/types": 30.3.0 + "@types/node": 25.5.2 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 30.3.0 + string-length: 4.0.2 + + jest-worker@30.3.0: + dependencies: + "@types/node": 25.5.2 + "@ungap/structured-clone": 1.3.0 + jest-util: 30.3.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@30.3.0(@types/node@25.5.2)(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)): + dependencies: + "@jest/core": 30.3.0(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)) + "@jest/types": 30.3.0 + import-local: 3.2.0 + jest-cli: 30.3.0(@types/node@25.5.2)(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)) + transitivePeerDependencies: + - "@types/node" + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + jiti@2.6.1: {} + js-tokens@4.0.0: {} + + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + jsesc@3.1.0: {} + json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + jsonwebtoken@9.0.3: dependencies: jws: 4.0.1 @@ -5577,6 +8861,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + leven@3.1.0: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -5584,6 +8870,8 @@ snapshots: libphonenumber-js@1.12.41: {} + lines-and-columns@1.2.4: {} + lint-staged@16.4.0: dependencies: commander: 14.0.3 @@ -5604,6 +8892,10 @@ snapshots: load-esm@1.0.3: {} + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -5620,6 +8912,8 @@ snapshots: lodash.isstring@4.0.1: {} + lodash.memoize@4.1.2: {} + lodash.once@4.1.1: {} lodash@4.17.23: {} @@ -5634,10 +8928,24 @@ snapshots: long@5.3.2: {} + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + lru.min@1.1.4: {} + make-dir@4.0.0: + dependencies: + semver: 7.7.4 + make-error@1.3.6: {} + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + math-intrinsics@1.1.0: {} media-typer@0.3.0: {} @@ -5646,6 +8954,10 @@ snapshots: merge-descriptors@2.0.0: {} + merge-stream@2.0.0: {} + + methods@1.1.2: {} + mime-db@1.52.0: {} mime-db@1.54.0: {} @@ -5658,6 +8970,10 @@ snapshots: dependencies: mime-db: 1.54.0 + mime@2.6.0: {} + + mimic-fn@2.1.0: {} + mimic-function@5.0.1: {} minimatch@10.2.5: @@ -5668,8 +8984,14 @@ snapshots: dependencies: brace-expansion: 1.1.13 + minimatch@9.0.9: + dependencies: + brace-expansion: 2.0.3 + minimist@1.2.8: {} + minipass@7.1.3: {} + mkdirp@1.0.4: {} ms@2.1.3: {} @@ -5697,14 +9019,26 @@ snapshots: dependencies: lru.min: 1.1.4 + napi-postinstall@0.3.4: {} + natural-compare@1.4.0: {} negotiator@1.0.0: {} + neo-async@2.6.2: {} + node-fetch-native@1.6.7: {} + node-int64@0.4.0: {} + + node-releases@2.0.37: {} + normalize-path@3.0.0: {} + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + nypm@0.6.5: dependencies: citty: 0.2.2 @@ -5725,6 +9059,10 @@ snapshots: dependencies: wrappy: 1.0.2 + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + onetime@7.0.0: dependencies: mimic-function: 5.0.1 @@ -5747,14 +9085,33 @@ snapshots: "@otplib/totp": 13.4.0 "@otplib/uri": 13.4.0 + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + p-locate@5.0.0: dependencies: p-limit: 3.1.0 + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + parse-json@5.2.0: + dependencies: + "@babel/code-frame": 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + parseurl@1.3.3: {} path-exists@4.0.0: {} @@ -5767,6 +9124,11 @@ snapshots: path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + path-to-regexp@8.4.2: {} pathe@2.0.3: {} @@ -5779,6 +9141,12 @@ snapshots: picomatch@4.0.4: {} + pirates@4.0.7: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + pkg-types@2.3.0: dependencies: confbox: 0.2.4 @@ -5791,6 +9159,12 @@ snapshots: prettier@3.8.1: {} + pretty-format@30.3.0: + dependencies: + "@jest/schemas": 30.0.5 + ansi-styles: 5.2.0 + react-is: 18.3.1 + prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3): dependencies: "@prisma/config": 7.6.0 @@ -5823,6 +9197,8 @@ snapshots: pure-rand@6.1.0: {} + pure-rand@7.0.1: {} + qs@6.15.0: dependencies: side-channel: 1.1.0 @@ -5846,6 +9222,8 @@ snapshots: react: 19.2.4 scheduler: 0.27.0 + react-is@18.3.1: {} + react@19.2.4: {} readable-stream@3.6.2: @@ -5864,8 +9242,16 @@ snapshots: remeda@2.33.4: {} + require-directory@2.1.1: {} + require-from-string@2.0.2: {} + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@5.0.0: {} + resolve@1.22.11: dependencies: is-core-module: 2.16.1 @@ -5905,6 +9291,8 @@ snapshots: scheduler@0.27.0: {} + semver@6.3.1: {} + semver@7.7.4: {} send@1.2.1: @@ -5976,6 +9364,8 @@ snapshots: sisteransi@1.0.5: {} + slash@3.0.0: {} + slice-ansi@7.1.2: dependencies: ansi-styles: 6.2.3 @@ -5986,6 +9376,11 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 @@ -5993,8 +9388,14 @@ snapshots: source-map@0.6.1: {} + sprintf-js@1.0.3: {} + sqlstring@2.3.3: {} + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + statuses@2.0.2: {} std-env@3.10.0: {} @@ -6003,6 +9404,23 @@ snapshots: string-argv@0.3.2: {} + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + string-width@7.2.0: dependencies: emoji-regex: 10.6.0 @@ -6018,26 +9436,78 @@ snapshots: dependencies: safe-buffer: 5.2.1 + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 strip-bom@3.0.0: {} + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} + strnum@2.2.2: {} strtok3@10.3.5: dependencies: "@tokenizer/token": 0.3.0 + superagent@10.3.0: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.4.3 + fast-safe-stringify: 2.1.1 + form-data: 4.0.5 + formidable: 3.5.4 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.15.0 + transitivePeerDependencies: + - supports-color + + supertest@7.2.2: + dependencies: + cookie-signature: 1.2.2 + methods: 1.1.2 + superagent: 10.3.0 + transitivePeerDependencies: + - supports-color + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} + synckit@0.11.12: + dependencies: + "@pkgr/core": 0.2.9 + + test-exclude@6.0.0: + dependencies: + "@istanbuljs/schema": 0.1.3 + glob: 7.2.3 + minimatch: 3.1.5 + thirty-two@1.0.2: {} tinyexec@1.0.4: {} + tmpl@1.0.5: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -6052,6 +9522,26 @@ snapshots: tree-kill@1.2.2: {} + ts-jest@29.4.9(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.5.2)(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)))(typescript@5.9.3): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + handlebars: 4.7.9 + jest: 30.3.0(@types/node@25.5.2)(ts-node@10.9.2(@types/node@25.5.2)(typescript@5.9.3)) + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.7.4 + type-fest: 4.41.0 + typescript: 5.9.3 + yargs-parser: 21.1.1 + optionalDependencies: + "@babel/core": 7.29.0 + "@jest/transform": 30.3.0 + "@jest/types": 30.3.0 + babel-jest: 30.3.0(@babel/core@7.29.0) + jest-util: 30.3.0 + ts-node-dev@2.0.0(@types/node@25.5.2)(typescript@5.9.3): dependencies: chokidar: 3.6.0 @@ -6110,6 +9600,12 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-detect@4.0.8: {} + + type-fest@0.21.3: {} + + type-fest@4.41.0: {} + type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -6125,6 +9621,9 @@ snapshots: typescript@5.9.3: {} + uglify-js@3.19.3: + optional: true + uid@2.0.2: dependencies: "@lukeed/csprng": 1.1.0 @@ -6135,6 +9634,36 @@ snapshots: unpipe@1.0.0: {} + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + "@unrs/resolver-binding-android-arm-eabi": 1.11.1 + "@unrs/resolver-binding-android-arm64": 1.11.1 + "@unrs/resolver-binding-darwin-arm64": 1.11.1 + "@unrs/resolver-binding-darwin-x64": 1.11.1 + "@unrs/resolver-binding-freebsd-x64": 1.11.1 + "@unrs/resolver-binding-linux-arm-gnueabihf": 1.11.1 + "@unrs/resolver-binding-linux-arm-musleabihf": 1.11.1 + "@unrs/resolver-binding-linux-arm64-gnu": 1.11.1 + "@unrs/resolver-binding-linux-arm64-musl": 1.11.1 + "@unrs/resolver-binding-linux-ppc64-gnu": 1.11.1 + "@unrs/resolver-binding-linux-riscv64-gnu": 1.11.1 + "@unrs/resolver-binding-linux-riscv64-musl": 1.11.1 + "@unrs/resolver-binding-linux-s390x-gnu": 1.11.1 + "@unrs/resolver-binding-linux-x64-gnu": 1.11.1 + "@unrs/resolver-binding-linux-x64-musl": 1.11.1 + "@unrs/resolver-binding-wasm32-wasi": 1.11.1 + "@unrs/resolver-binding-win32-arm64-msvc": 1.11.1 + "@unrs/resolver-binding-win32-ia32-msvc": 1.11.1 + "@unrs/resolver-binding-win32-x64-msvc": 1.11.1 + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -6143,6 +9672,12 @@ snapshots: v8-compile-cache-lib@3.0.1: {} + v8-to-istanbul@9.3.0: + dependencies: + "@jridgewell/trace-mapping": 0.3.31 + "@types/istanbul-lib-coverage": 2.0.6 + convert-source-map: 2.0.0 + valibot@1.2.0(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 @@ -6151,12 +9686,30 @@ snapshots: vary@1.1.2: {} + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + which@2.0.2: dependencies: isexe: 2.0.0 word-wrap@1.2.5: {} + wordwrap@1.0.0: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + wrap-ansi@9.0.2: dependencies: ansi-styles: 6.2.3 @@ -6165,10 +9718,31 @@ snapshots: wrappy@1.0.2: {} + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + xtend@4.0.2: {} + y18n@5.0.8: {} + + yallist@3.1.1: {} + yaml@2.8.3: {} + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + yn@3.1.1: {} yocto-queue@0.1.0: {} From e84bef07b422ba160cd12e31b23e361ae963e8a7 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sun, 5 Apr 2026 01:07:05 +0800 Subject: [PATCH 21/22] fix(api-prisma): initialize prisma client with pg adapter --- apps/api/package.json | 2 + apps/api/src/prisma/prisma.service.ts | 15 ++ pnpm-lock.yaml | 192 ++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) diff --git a/apps/api/package.json b/apps/api/package.json index 2b3fe6d..53898ec 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -39,10 +39,12 @@ "@nestjs/jwt": "^11.0.2", "@nestjs/platform-express": "^11.1.18", "@otplib/preset-default": "^12.0.1", + "@prisma/adapter-pg": "^7.6.0", "@prisma/client": "^7.6.0", "class-transformer": "^0.5.1", "class-validator": "^0.15.1", "otplib": "^13.4.0", + "pg": "^8.20.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.2" } diff --git a/apps/api/src/prisma/prisma.service.ts b/apps/api/src/prisma/prisma.service.ts index 72e40f6..27e6013 100644 --- a/apps/api/src/prisma/prisma.service.ts +++ b/apps/api/src/prisma/prisma.service.ts @@ -1,8 +1,23 @@ import { Injectable, OnModuleDestroy, OnModuleInit } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { PrismaPg } from "@prisma/adapter-pg"; import { PrismaClient } from "../../generated/prisma/client"; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { + constructor(configService: ConfigService) { + const connectionString = configService.get("DATABASE_URL"); + if (!connectionString) { + throw new Error("缺少数据库连接配置 DATABASE_URL"); + } + + super({ + adapter: new PrismaPg({ + connectionString + }) + }); + } + async onModuleInit(): Promise { await this.$connect(); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a87e0fd..364478b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,6 +55,9 @@ importers: "@otplib/preset-default": specifier: ^12.0.1 version: 12.0.1 + "@prisma/adapter-pg": + specifier: ^7.6.0 + version: 7.6.0 "@prisma/client": specifier: ^7.6.0 version: 7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) @@ -67,6 +70,9 @@ importers: otplib: specifier: ^13.4.0 version: 13.4.0 + pg: + specifier: ^8.20.0 + version: 8.20.0 reflect-metadata: specifier: ^0.2.2 version: 0.2.2 @@ -1248,6 +1254,12 @@ packages: } engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } + "@prisma/adapter-pg@7.6.0": + resolution: + { + integrity: sha512-BjHNmJqqa42NqJSDPnXUfwUofWo8LJY7Ui2gqxN4DmAOb+H/gGKv+hln2Xq/1kSJXPW5AXMXuNiPDMpywvyIOw== + } + "@prisma/client-runtime-utils@7.6.0": resolution: { @@ -1293,6 +1305,12 @@ packages: integrity: sha512-ffHlQuKXZiaDt9Go0OnCTdJZrHxK0k7omJKNV86/VjpsXu5EIHZLK0T7JSWgvNlJwh56kW9JFu9v0qJciFzepg== } + "@prisma/driver-adapter-utils@7.6.0": + resolution: + { + integrity: sha512-D8j3p0RnhLuufMaRLX6QqtGgPC5Ao3l5oFP6Q5AL0rTHi4vna+NzGEipwCsfvcSvaGFCbsH3lsTMbb4WvY+ovA== + } + "@prisma/engines-version@7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711": resolution: { @@ -2087,6 +2105,12 @@ packages: integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg== } + "@types/pg@8.20.0": + resolution: + { + integrity: sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow== + } + "@types/qs@6.15.0": resolution: { @@ -4729,6 +4753,64 @@ packages: integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== } + pg-cloudflare@1.3.0: + resolution: + { + integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ== + } + + pg-connection-string@2.12.0: + resolution: + { + integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ== + } + + pg-int8@1.0.1: + resolution: + { + integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== + } + engines: { node: ">=4.0.0" } + + pg-pool@3.13.0: + resolution: + { + integrity: sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA== + } + peerDependencies: + pg: ">=8.0" + + pg-protocol@1.13.0: + resolution: + { + integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w== + } + + pg-types@2.2.0: + resolution: + { + integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== + } + engines: { node: ">=4" } + + pg@8.20.0: + resolution: + { + integrity: sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA== + } + engines: { node: ">= 16.0.0" } + peerDependencies: + pg-native: ">=3.0.1" + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: + { + integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug== + } + picocolors@1.1.1: resolution: { @@ -4769,6 +4851,41 @@ packages: integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig== } + postgres-array@2.0.0: + resolution: + { + integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== + } + engines: { node: ">=4" } + + postgres-array@3.0.4: + resolution: + { + integrity: sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ== + } + engines: { node: ">=12" } + + postgres-bytea@1.0.1: + resolution: + { + integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ== + } + engines: { node: ">=0.10.0" } + + postgres-date@1.0.7: + resolution: + { + integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== + } + engines: { node: ">=0.10.0" } + + postgres-interval@1.2.0: + resolution: + { + integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== + } + engines: { node: ">=0.10.0" } + postgres@3.4.7: resolution: { @@ -5164,6 +5281,13 @@ packages: } engines: { node: ">=0.10.0" } + split2@4.2.0: + resolution: + { + integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + } + engines: { node: ">= 10.x" } + sprintf-js@1.0.3: resolution: { @@ -6869,6 +6993,15 @@ snapshots: "@pkgr/core@0.2.9": {} + "@prisma/adapter-pg@7.6.0": + dependencies: + "@prisma/driver-adapter-utils": 7.6.0 + "@types/pg": 8.20.0 + pg: 8.20.0 + postgres-array: 3.0.4 + transitivePeerDependencies: + - pg-native + "@prisma/client-runtime-utils@7.6.0": {} "@prisma/client@7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)": @@ -6913,6 +7046,10 @@ snapshots: transitivePeerDependencies: - typescript + "@prisma/driver-adapter-utils@7.6.0": + dependencies: + "@prisma/debug": 7.6.0 + "@prisma/engines-version@7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711": {} "@prisma/engines@7.6.0": @@ -7496,6 +7633,12 @@ snapshots: dependencies: "@types/express": 5.0.6 + "@types/pg@8.20.0": + dependencies: + "@types/node": 25.5.2 + pg-protocol: 1.13.0 + pg-types: 2.2.0 + "@types/qs@6.15.0": {} "@types/range-parser@1.2.7": {} @@ -9135,6 +9278,41 @@ snapshots: perfect-debounce@1.0.0: {} + pg-cloudflare@1.3.0: + optional: true + + pg-connection-string@2.12.0: {} + + pg-int8@1.0.1: {} + + pg-pool@3.13.0(pg@8.20.0): + dependencies: + pg: 8.20.0 + + pg-protocol@1.13.0: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.1 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg@8.20.0: + dependencies: + pg-connection-string: 2.12.0 + pg-pool: 3.13.0(pg@8.20.0) + pg-protocol: 1.13.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.3.0 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + picocolors@1.1.1: {} picomatch@2.3.2: {} @@ -9153,6 +9331,18 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 + postgres-array@2.0.0: {} + + postgres-array@3.0.4: {} + + postgres-bytea@1.0.1: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + postgres@3.4.7: {} prelude-ls@1.2.1: {} @@ -9388,6 +9578,8 @@ snapshots: source-map@0.6.1: {} + split2@4.2.0: {} + sprintf-js@1.0.3: {} sqlstring@2.3.3: {} From 99e5622234011dacf213adb0dd7f9da388940a42 Mon Sep 17 00:00:00 2001 From: Yaosanqi137 Date: Sun, 5 Apr 2026 02:14:20 +0800 Subject: [PATCH 22/22] fix(ci): ensure prisma client generation and auth deps for typecheck --- apps/api/package.json | 7 +++ pnpm-lock.yaml | 116 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/apps/api/package.json b/apps/api/package.json index 53898ec..66837c0 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -6,6 +6,9 @@ "prisma:generate": "prisma generate", "prisma:format": "prisma format", "prisma:validate": "prisma validate", + "prebuild": "pnpm run prisma:generate", + "pretypecheck": "pnpm run prisma:generate", + "pretest": "pnpm run prisma:generate", "start": "node dist/main.js", "start:dev": "ts-node-dev --respawn --transpile-only src/main.ts", "build": "tsc -p tsconfig.build.json", @@ -37,6 +40,7 @@ "@nestjs/config": "^4.0.3", "@nestjs/core": "^11.1.18", "@nestjs/jwt": "^11.0.2", + "@nestjs/passport": "^11.0.5", "@nestjs/platform-express": "^11.1.18", "@otplib/preset-default": "^12.0.1", "@prisma/adapter-pg": "^7.6.0", @@ -44,6 +48,9 @@ "class-transformer": "^0.5.1", "class-validator": "^0.15.1", "otplib": "^13.4.0", + "passport": "^0.7.0", + "passport-github2": "^0.1.12", + "passport-oauth2": "^1.8.0", "pg": "^8.20.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 364478b..799a176 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,6 +49,9 @@ importers: "@nestjs/jwt": specifier: ^11.0.2 version: 11.0.2(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)) + "@nestjs/passport": + specifier: ^11.0.5 + version: 11.0.5(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) "@nestjs/platform-express": specifier: ^11.1.18 version: 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18) @@ -70,6 +73,15 @@ importers: otplib: specifier: ^13.4.0 version: 13.4.0 + passport: + specifier: ^0.7.0 + version: 0.7.0 + passport-github2: + specifier: ^0.1.12 + version: 0.1.12 + passport-oauth2: + specifier: ^1.8.0 + version: 1.8.0 pg: specifier: ^8.20.0 version: 8.20.0 @@ -1124,6 +1136,15 @@ packages: peerDependencies: "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + "@nestjs/passport@11.0.5": + resolution: + { + integrity: sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ== + } + peerDependencies: + "@nestjs/common": ^10.0.0 || ^11.0.0 + passport: ^0.5.0 || ^0.6.0 || ^0.7.0 + "@nestjs/platform-express@11.1.18": resolution: { @@ -2535,6 +2556,13 @@ packages: } engines: { node: 18 || 20 || >=22 } + base64url@3.0.1: + resolution: + { + integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + } + engines: { node: ">=6.0.0" } + baseline-browser-mapping@2.10.14: resolution: { @@ -4579,6 +4607,12 @@ packages: engines: { node: ">=18" } hasBin: true + oauth@0.10.2: + resolution: + { + integrity: sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q== + } + object-assign@4.1.1: resolution: { @@ -4694,6 +4728,34 @@ packages: } engines: { node: ">= 0.8" } + passport-github2@0.1.12: + resolution: + { + integrity: sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw== + } + engines: { node: ">= 0.8.0" } + + passport-oauth2@1.8.0: + resolution: + { + integrity: sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA== + } + engines: { node: ">= 0.4.0" } + + passport-strategy@1.0.0: + resolution: + { + integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA== + } + engines: { node: ">= 0.4.0" } + + passport@0.7.0: + resolution: + { + integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ== + } + engines: { node: ">= 0.4.0" } + path-exists@4.0.0: resolution: { @@ -4747,6 +4809,12 @@ packages: integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== } + pause@0.0.1: + resolution: + { + integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg== + } + perfect-debounce@1.0.0: resolution: { @@ -5679,6 +5747,12 @@ packages: engines: { node: ">=0.8.0" } hasBin: true + uid2@0.0.4: + resolution: + { + integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA== + } + uid@2.0.2: resolution: { @@ -5733,6 +5807,13 @@ packages: integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== } + utils-merge@1.0.1: + resolution: + { + integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + } + engines: { node: ">= 0.4.0" } + v8-compile-cache-lib@3.0.1: resolution: { @@ -6912,6 +6993,11 @@ snapshots: "@types/jsonwebtoken": 9.0.10 jsonwebtoken: 9.0.3 + "@nestjs/passport@11.0.5(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)": + dependencies: + "@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + passport: 0.7.0 + "@nestjs/platform-express@11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)": dependencies: "@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -7867,6 +7953,8 @@ snapshots: balanced-match@4.0.4: {} + base64url@3.0.1: {} + baseline-browser-mapping@2.10.14: {} better-result@2.7.0: @@ -9188,6 +9276,8 @@ snapshots: pathe: 2.0.3 tinyexec: 1.0.4 + oauth@0.10.2: {} + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -9257,6 +9347,26 @@ snapshots: parseurl@1.3.3: {} + passport-github2@0.1.12: + dependencies: + passport-oauth2: 1.8.0 + + passport-oauth2@1.8.0: + dependencies: + base64url: 3.0.1 + oauth: 0.10.2 + passport-strategy: 1.0.0 + uid2: 0.0.4 + utils-merge: 1.0.1 + + passport-strategy@1.0.0: {} + + passport@0.7.0: + dependencies: + passport-strategy: 1.0.0 + pause: 0.0.1 + utils-merge: 1.0.1 + path-exists@4.0.0: {} path-expression-matcher@1.2.1: {} @@ -9276,6 +9386,8 @@ snapshots: pathe@2.0.3: {} + pause@0.0.1: {} + perfect-debounce@1.0.0: {} pg-cloudflare@1.3.0: @@ -9816,6 +9928,8 @@ snapshots: uglify-js@3.19.3: optional: true + uid2@0.0.4: {} + uid@2.0.2: dependencies: "@lukeed/csprng": 1.1.0 @@ -9862,6 +9976,8 @@ snapshots: util-deprecate@1.0.2: {} + utils-merge@1.0.1: {} + v8-compile-cache-lib@3.0.1: {} v8-to-istanbul@9.3.0: