feat(GOV-009): 建立前端 OpenAPI 客户端边界
This commit is contained in:
@@ -24,7 +24,7 @@
|
|||||||
<!-- gitnexus:start -->
|
<!-- gitnexus:start -->
|
||||||
# GitNexus — Code Intelligence
|
# GitNexus — Code Intelligence
|
||||||
|
|
||||||
This project is indexed by GitNexus as **a-share-analysis** (1372 symbols, 1907 relationships, 42 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
|
This project is indexed by GitNexus as **a-share-analysis** (1440 symbols, 2018 relationships, 44 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
|
||||||
|
|
||||||
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
|
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -19,12 +19,14 @@
|
|||||||
"schema:verify": "node tools/database/schema-baseline.mjs verify",
|
"schema:verify": "node tools/database/schema-baseline.mjs verify",
|
||||||
"governance:start": "node tools/governance/start.mjs",
|
"governance:start": "node tools/governance/start.mjs",
|
||||||
"governance:finish": "node tools/governance/finish.mjs",
|
"governance:finish": "node tools/governance/finish.mjs",
|
||||||
|
"check:contracts": "pnpm --filter @a-share-analysis/server openapi:generate && pnpm --filter @a-share-analysis/web api:generate && pnpm exec prettier --write server/openapi/openapi.json web/src/api/generated/schema.ts && node tools/boundaries/verify-project-boundaries.mjs",
|
||||||
|
"test:boundaries": "node --test tools/boundaries/verify-project-boundaries.test.mjs",
|
||||||
"lint": "pnpm --filter @a-share-analysis/web --if-present lint && pnpm --filter @a-share-analysis/server --if-present lint",
|
"lint": "pnpm --filter @a-share-analysis/web --if-present lint && pnpm --filter @a-share-analysis/server --if-present lint",
|
||||||
"format:check": "pnpm exec prettier --check .",
|
"format:check": "pnpm exec prettier --check .",
|
||||||
"typecheck": "pnpm --filter @a-share-analysis/web --if-present typecheck && pnpm --filter @a-share-analysis/server --if-present typecheck",
|
"typecheck": "pnpm --filter @a-share-analysis/web --if-present typecheck && pnpm --filter @a-share-analysis/server --if-present typecheck",
|
||||||
"test": "pnpm test:runtime && pnpm test:governance && pnpm test:mysql-baseline && pnpm test:environment-baseline && pnpm test:schema-baseline && pnpm --filter @a-share-analysis/web --if-present test && pnpm --filter @a-share-analysis/server --if-present test",
|
"test": "pnpm test:runtime && pnpm test:governance && pnpm test:mysql-baseline && pnpm test:environment-baseline && pnpm test:schema-baseline && pnpm test:boundaries && pnpm --filter @a-share-analysis/web --if-present test && pnpm --filter @a-share-analysis/server --if-present test",
|
||||||
"build": "pnpm --filter @a-share-analysis/web --if-present build && pnpm --filter @a-share-analysis/server --if-present build",
|
"build": "pnpm --filter @a-share-analysis/web --if-present build && pnpm --filter @a-share-analysis/server --if-present build",
|
||||||
"verify": "pnpm check:runtime && pnpm lint && pnpm format:check && pnpm typecheck && pnpm test && pnpm build"
|
"verify": "pnpm check:runtime && pnpm check:contracts && pnpm lint && pnpm format:check && pnpm typecheck && pnpm test && pnpm build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "9.39.5",
|
"@eslint/js": "9.39.5",
|
||||||
@@ -32,6 +34,7 @@
|
|||||||
"eslint-plugin-vue": "9.33.0",
|
"eslint-plugin-vue": "9.33.0",
|
||||||
"globals": "17.11.0",
|
"globals": "17.11.0",
|
||||||
"prettier": "3.9.6",
|
"prettier": "3.9.6",
|
||||||
|
"typescript": "5.9.3",
|
||||||
"typescript-eslint": "8.46.2"
|
"typescript-eslint": "8.46.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+158
-24
@@ -23,6 +23,9 @@ importers:
|
|||||||
prettier:
|
prettier:
|
||||||
specifier: 3.9.6
|
specifier: 3.9.6
|
||||||
version: 3.9.6
|
version: 3.9.6
|
||||||
|
typescript:
|
||||||
|
specifier: 5.9.3
|
||||||
|
version: 5.9.3
|
||||||
typescript-eslint:
|
typescript-eslint:
|
||||||
specifier: 8.46.2
|
specifier: 8.46.2
|
||||||
version: 8.46.2(eslint@9.39.5)(typescript@5.9.3)
|
version: 8.46.2(eslint@9.39.5)(typescript@5.9.3)
|
||||||
@@ -99,6 +102,9 @@ importers:
|
|||||||
naive-ui:
|
naive-ui:
|
||||||
specifier: 2.44.1
|
specifier: 2.44.1
|
||||||
version: 2.44.1(vue@3.5.41(typescript@5.9.3))
|
version: 2.44.1(vue@3.5.41(typescript@5.9.3))
|
||||||
|
openapi-fetch:
|
||||||
|
specifier: 0.17.0
|
||||||
|
version: 0.17.0
|
||||||
pinia:
|
pinia:
|
||||||
specifier: 3.0.4
|
specifier: 3.0.4
|
||||||
version: 3.0.4(typescript@5.9.3)(vue@3.5.41(typescript@5.9.3))
|
version: 3.0.4(typescript@5.9.3)(vue@3.5.41(typescript@5.9.3))
|
||||||
@@ -130,6 +136,9 @@ importers:
|
|||||||
katex:
|
katex:
|
||||||
specifier: 0.18.4
|
specifier: 0.18.4
|
||||||
version: 0.18.4
|
version: 0.18.4
|
||||||
|
openapi-typescript:
|
||||||
|
specifier: 7.13.0
|
||||||
|
version: 7.13.0(typescript@5.9.3)
|
||||||
sass:
|
sass:
|
||||||
specifier: 1.102.0
|
specifier: 1.102.0
|
||||||
version: 1.102.0
|
version: 1.102.0
|
||||||
@@ -872,6 +881,16 @@ packages:
|
|||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
'@redocly/ajv@8.11.2':
|
||||||
|
resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==}
|
||||||
|
|
||||||
|
'@redocly/config@0.22.0':
|
||||||
|
resolution: {integrity: sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==}
|
||||||
|
|
||||||
|
'@redocly/openapi-core@1.34.19':
|
||||||
|
resolution: {integrity: sha512-o/0VgsBXgwcY1lyeqcVtSGdTQAPnVggo0fbFVPlxl5XVDKUcVH0OLRqt3CbkwByT5FU305E0iE0O7MzThjDblw==}
|
||||||
|
engines: {node: '>=18.17.0', npm: '>=9.5.0'}
|
||||||
|
|
||||||
'@rollup/rollup-android-arm-eabi@4.62.4':
|
'@rollup/rollup-android-arm-eabi@4.62.4':
|
||||||
resolution: {integrity: sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==}
|
resolution: {integrity: sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
@@ -1492,6 +1511,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
change-case@5.4.4:
|
||||||
|
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
|
||||||
|
|
||||||
chardet@2.2.0:
|
chardet@2.2.0:
|
||||||
resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==}
|
resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==}
|
||||||
|
|
||||||
@@ -1546,6 +1568,9 @@ packages:
|
|||||||
color-name@1.1.4:
|
color-name@1.1.4:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
|
||||||
|
colorette@1.4.0:
|
||||||
|
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
|
||||||
|
|
||||||
combined-stream@1.0.8:
|
combined-stream@1.0.8:
|
||||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@@ -2140,6 +2165,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
||||||
engines: {node: '>=0.8.19'}
|
engines: {node: '>=0.8.19'}
|
||||||
|
|
||||||
|
index-to-position@1.2.0:
|
||||||
|
resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
inherits@2.0.4:
|
inherits@2.0.4:
|
||||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||||
|
|
||||||
@@ -2227,6 +2256,10 @@ packages:
|
|||||||
js-cookie@3.0.8:
|
js-cookie@3.0.8:
|
||||||
resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==}
|
resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==}
|
||||||
|
|
||||||
|
js-levenshtein@1.1.6:
|
||||||
|
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
js-tokens@4.0.0:
|
js-tokens@4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
@@ -2405,6 +2438,10 @@ packages:
|
|||||||
minimatch@3.1.5:
|
minimatch@3.1.5:
|
||||||
resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
|
resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
|
||||||
|
|
||||||
|
minimatch@5.1.9:
|
||||||
|
resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
minimatch@9.0.9:
|
minimatch@9.0.9:
|
||||||
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
|
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
|
||||||
engines: {node: '>=16 || 14 >=14.17'}
|
engines: {node: '>=16 || 14 >=14.17'}
|
||||||
@@ -2507,6 +2544,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
|
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
openapi-fetch@0.17.0:
|
||||||
|
resolution: {integrity: sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig==}
|
||||||
|
|
||||||
|
openapi-typescript-helpers@0.1.0:
|
||||||
|
resolution: {integrity: sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==}
|
||||||
|
|
||||||
|
openapi-typescript@7.13.0:
|
||||||
|
resolution: {integrity: sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
typescript: ^5.x
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -2534,6 +2583,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
|
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
parse-json@8.3.0:
|
||||||
|
resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
parse5@7.3.0:
|
parse5@7.3.0:
|
||||||
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
|
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
|
||||||
|
|
||||||
@@ -2907,6 +2960,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==}
|
resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==}
|
||||||
engines: {node: '>=14.18.0'}
|
engines: {node: '>=14.18.0'}
|
||||||
|
|
||||||
|
supports-color@10.2.2:
|
||||||
|
resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
supports-color@7.2.0:
|
supports-color@7.2.0:
|
||||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -3074,6 +3131,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
type-fest@4.41.0:
|
||||||
|
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
type-is@1.6.18:
|
type-is@1.6.18:
|
||||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@@ -3181,6 +3242,9 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
browserslist: '>= 4.21.0'
|
browserslist: '>= 4.21.0'
|
||||||
|
|
||||||
|
uri-js-replace@1.0.1:
|
||||||
|
resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==}
|
||||||
|
|
||||||
uri-js@4.4.1:
|
uri-js@4.4.1:
|
||||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||||
|
|
||||||
@@ -3420,6 +3484,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
yaml-ast-parser@0.0.43:
|
||||||
|
resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
|
||||||
|
|
||||||
yargs-parser@21.1.1:
|
yargs-parser@21.1.1:
|
||||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -3642,7 +3709,7 @@ snapshots:
|
|||||||
'@eslint/config-array@0.21.2':
|
'@eslint/config-array@0.21.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint/object-schema': 2.1.7
|
'@eslint/object-schema': 2.1.7
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
minimatch: 3.1.5
|
minimatch: 3.1.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -3658,7 +3725,7 @@ snapshots:
|
|||||||
'@eslint/eslintrc@3.3.6':
|
'@eslint/eslintrc@3.3.6':
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv: 6.15.0
|
ajv: 6.15.0
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
espree: 10.4.0
|
espree: 10.4.0
|
||||||
globals: 14.0.0
|
globals: 14.0.0
|
||||||
ignore: 5.3.2
|
ignore: 5.3.2
|
||||||
@@ -4076,6 +4143,29 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
playwright: 1.62.1
|
playwright: 1.62.1
|
||||||
|
|
||||||
|
'@redocly/ajv@8.11.2':
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
json-schema-traverse: 1.0.0
|
||||||
|
require-from-string: 2.0.2
|
||||||
|
uri-js-replace: 1.0.1
|
||||||
|
|
||||||
|
'@redocly/config@0.22.0': {}
|
||||||
|
|
||||||
|
'@redocly/openapi-core@1.34.19(supports-color@10.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@redocly/ajv': 8.11.2
|
||||||
|
'@redocly/config': 0.22.0
|
||||||
|
colorette: 1.4.0
|
||||||
|
https-proxy-agent: 7.0.6(supports-color@10.2.2)
|
||||||
|
js-levenshtein: 1.1.6
|
||||||
|
js-yaml: 4.3.1
|
||||||
|
minimatch: 5.1.9
|
||||||
|
pluralize: 8.0.0
|
||||||
|
yaml-ast-parser: 0.0.43
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@rollup/rollup-android-arm-eabi@4.62.4':
|
'@rollup/rollup-android-arm-eabi@4.62.4':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -4157,7 +4247,7 @@ snapshots:
|
|||||||
|
|
||||||
'@tokenizer/inflate@0.4.1':
|
'@tokenizer/inflate@0.4.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
token-types: 6.1.2
|
token-types: 6.1.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -4234,7 +4324,7 @@ snapshots:
|
|||||||
'@typescript-eslint/types': 8.46.2
|
'@typescript-eslint/types': 8.46.2
|
||||||
'@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
|
'@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
|
||||||
'@typescript-eslint/visitor-keys': 8.46.2
|
'@typescript-eslint/visitor-keys': 8.46.2
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -4244,7 +4334,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3)
|
'@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3)
|
||||||
'@typescript-eslint/types': 8.46.2
|
'@typescript-eslint/types': 8.46.2
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -4263,7 +4353,7 @@ snapshots:
|
|||||||
'@typescript-eslint/types': 8.46.2
|
'@typescript-eslint/types': 8.46.2
|
||||||
'@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
|
'@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
|
||||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.5)(typescript@5.9.3)
|
'@typescript-eslint/utils': 8.46.2(eslint@9.39.5)(typescript@5.9.3)
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5
|
||||||
ts-api-utils: 2.5.0(typescript@5.9.3)
|
ts-api-utils: 2.5.0(typescript@5.9.3)
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
@@ -4278,7 +4368,7 @@ snapshots:
|
|||||||
'@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3)
|
'@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3)
|
||||||
'@typescript-eslint/types': 8.46.2
|
'@typescript-eslint/types': 8.46.2
|
||||||
'@typescript-eslint/visitor-keys': 8.46.2
|
'@typescript-eslint/visitor-keys': 8.46.2
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
fast-glob: 3.3.3
|
fast-glob: 3.3.3
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
minimatch: 9.0.9
|
minimatch: 9.0.9
|
||||||
@@ -4653,7 +4743,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
bytes: 3.1.2
|
bytes: 3.1.2
|
||||||
content-type: 2.0.0
|
content-type: 2.0.0
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
http-errors: 2.0.1
|
http-errors: 2.0.1
|
||||||
iconv-lite: 0.7.3
|
iconv-lite: 0.7.3
|
||||||
on-finished: 2.4.1
|
on-finished: 2.4.1
|
||||||
@@ -4744,6 +4834,8 @@ snapshots:
|
|||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
supports-color: 7.2.0
|
supports-color: 7.2.0
|
||||||
|
|
||||||
|
change-case@5.4.4: {}
|
||||||
|
|
||||||
chardet@2.2.0: {}
|
chardet@2.2.0: {}
|
||||||
|
|
||||||
check-error@2.1.3: {}
|
check-error@2.1.3: {}
|
||||||
@@ -4788,6 +4880,8 @@ snapshots:
|
|||||||
|
|
||||||
color-name@1.1.4: {}
|
color-name@1.1.4: {}
|
||||||
|
|
||||||
|
colorette@1.4.0: {}
|
||||||
|
|
||||||
combined-stream@1.0.8:
|
combined-stream@1.0.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
delayed-stream: 1.0.0
|
delayed-stream: 1.0.0
|
||||||
@@ -4886,9 +4980,11 @@ snapshots:
|
|||||||
|
|
||||||
dayjs@1.11.21: {}
|
dayjs@1.11.21: {}
|
||||||
|
|
||||||
debug@4.4.3:
|
debug@4.4.3(supports-color@10.2.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
supports-color: 10.2.2
|
||||||
|
|
||||||
decimal.js@10.6.0: {}
|
decimal.js@10.6.0: {}
|
||||||
|
|
||||||
@@ -5068,7 +5164,7 @@ snapshots:
|
|||||||
ajv: 6.15.0
|
ajv: 6.15.0
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
escape-string-regexp: 4.0.0
|
escape-string-regexp: 4.0.0
|
||||||
eslint-scope: 8.4.0
|
eslint-scope: 8.4.0
|
||||||
eslint-visitor-keys: 4.2.1
|
eslint-visitor-keys: 4.2.1
|
||||||
@@ -5140,7 +5236,7 @@ snapshots:
|
|||||||
content-type: 1.0.5
|
content-type: 1.0.5
|
||||||
cookie: 0.7.2
|
cookie: 0.7.2
|
||||||
cookie-signature: 1.2.2
|
cookie-signature: 1.2.2
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
depd: 2.0.0
|
depd: 2.0.0
|
||||||
encodeurl: 2.0.0
|
encodeurl: 2.0.0
|
||||||
escape-html: 1.0.3
|
escape-html: 1.0.3
|
||||||
@@ -5210,7 +5306,7 @@ snapshots:
|
|||||||
|
|
||||||
finalhandler@2.1.1:
|
finalhandler@2.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
encodeurl: 2.0.0
|
encodeurl: 2.0.0
|
||||||
escape-html: 1.0.3
|
escape-html: 1.0.3
|
||||||
on-finished: 2.4.1
|
on-finished: 2.4.1
|
||||||
@@ -5402,14 +5498,14 @@ snapshots:
|
|||||||
http-proxy-agent@7.0.2:
|
http-proxy-agent@7.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
agent-base: 7.1.4
|
agent-base: 7.1.4
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
https-proxy-agent@7.0.6:
|
https-proxy-agent@7.0.6(supports-color@10.2.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
agent-base: 7.1.4
|
agent-base: 7.1.4
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -5436,6 +5532,8 @@ snapshots:
|
|||||||
|
|
||||||
imurmurhash@0.1.4: {}
|
imurmurhash@0.1.4: {}
|
||||||
|
|
||||||
|
index-to-position@1.2.0: {}
|
||||||
|
|
||||||
inherits@2.0.4: {}
|
inherits@2.0.4: {}
|
||||||
|
|
||||||
ini@1.3.8: {}
|
ini@1.3.8: {}
|
||||||
@@ -5444,7 +5542,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@ioredis/commands': 1.10.0
|
'@ioredis/commands': 1.10.0
|
||||||
cluster-key-slot: 1.1.1
|
cluster-key-slot: 1.1.1
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
denque: 2.1.0
|
denque: 2.1.0
|
||||||
redis-errors: 1.2.0
|
redis-errors: 1.2.0
|
||||||
redis-parser: 3.0.0
|
redis-parser: 3.0.0
|
||||||
@@ -5512,6 +5610,8 @@ snapshots:
|
|||||||
|
|
||||||
js-cookie@3.0.8: {}
|
js-cookie@3.0.8: {}
|
||||||
|
|
||||||
|
js-levenshtein@1.1.6: {}
|
||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
js-tokens@9.0.1: {}
|
js-tokens@9.0.1: {}
|
||||||
@@ -5531,7 +5631,7 @@ snapshots:
|
|||||||
decimal.js: 10.6.0
|
decimal.js: 10.6.0
|
||||||
html-encoding-sniffer: 4.0.0
|
html-encoding-sniffer: 4.0.0
|
||||||
http-proxy-agent: 7.0.2
|
http-proxy-agent: 7.0.2
|
||||||
https-proxy-agent: 7.0.6
|
https-proxy-agent: 7.0.6(supports-color@10.2.2)
|
||||||
is-potential-custom-element-name: 1.0.1
|
is-potential-custom-element-name: 1.0.1
|
||||||
nwsapi: 2.2.24
|
nwsapi: 2.2.24
|
||||||
parse5: 7.3.0
|
parse5: 7.3.0
|
||||||
@@ -5670,6 +5770,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 1.1.18
|
brace-expansion: 1.1.18
|
||||||
|
|
||||||
|
minimatch@5.1.9:
|
||||||
|
dependencies:
|
||||||
|
brace-expansion: 2.1.4
|
||||||
|
|
||||||
minimatch@9.0.9:
|
minimatch@9.0.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 2.1.4
|
brace-expansion: 2.1.4
|
||||||
@@ -5775,6 +5879,22 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-fn: 2.1.0
|
mimic-fn: 2.1.0
|
||||||
|
|
||||||
|
openapi-fetch@0.17.0:
|
||||||
|
dependencies:
|
||||||
|
openapi-typescript-helpers: 0.1.0
|
||||||
|
|
||||||
|
openapi-typescript-helpers@0.1.0: {}
|
||||||
|
|
||||||
|
openapi-typescript@7.13.0(typescript@5.9.3):
|
||||||
|
dependencies:
|
||||||
|
'@redocly/openapi-core': 1.34.19(supports-color@10.2.2)
|
||||||
|
ansi-colors: 4.1.3
|
||||||
|
change-case: 5.4.4
|
||||||
|
parse-json: 8.3.0
|
||||||
|
supports-color: 10.2.2
|
||||||
|
typescript: 5.9.3
|
||||||
|
yargs-parser: 21.1.1
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
deep-is: 0.1.4
|
deep-is: 0.1.4
|
||||||
@@ -5817,6 +5937,12 @@ snapshots:
|
|||||||
json-parse-even-better-errors: 2.3.1
|
json-parse-even-better-errors: 2.3.1
|
||||||
lines-and-columns: 1.2.4
|
lines-and-columns: 1.2.4
|
||||||
|
|
||||||
|
parse-json@8.3.0:
|
||||||
|
dependencies:
|
||||||
|
'@babel/code-frame': 7.29.7
|
||||||
|
index-to-position: 1.2.0
|
||||||
|
type-fest: 4.41.0
|
||||||
|
|
||||||
parse5@7.3.0:
|
parse5@7.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
entities: 6.0.1
|
entities: 6.0.1
|
||||||
@@ -5985,7 +6111,7 @@ snapshots:
|
|||||||
|
|
||||||
router@2.2.0:
|
router@2.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
depd: 2.0.0
|
depd: 2.0.0
|
||||||
is-promise: 4.0.0
|
is-promise: 4.0.0
|
||||||
parseurl: 1.3.3
|
parseurl: 1.3.3
|
||||||
@@ -6042,7 +6168,7 @@ snapshots:
|
|||||||
|
|
||||||
send@1.2.1:
|
send@1.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
encodeurl: 2.0.0
|
encodeurl: 2.0.0
|
||||||
escape-html: 1.0.3
|
escape-html: 1.0.3
|
||||||
etag: 1.8.1
|
etag: 1.8.1
|
||||||
@@ -6189,7 +6315,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
component-emitter: 1.3.1
|
component-emitter: 1.3.1
|
||||||
cookiejar: 2.1.4
|
cookiejar: 2.1.4
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
fast-safe-stringify: 2.1.1
|
fast-safe-stringify: 2.1.1
|
||||||
form-data: 4.0.6
|
form-data: 4.0.6
|
||||||
formidable: 3.5.4
|
formidable: 3.5.4
|
||||||
@@ -6211,6 +6337,8 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
supports-color@10.2.2: {}
|
||||||
|
|
||||||
supports-color@7.2.0:
|
supports-color@7.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-flag: 4.0.0
|
has-flag: 4.0.0
|
||||||
@@ -6333,6 +6461,8 @@ snapshots:
|
|||||||
|
|
||||||
type-fest@0.20.2: {}
|
type-fest@0.20.2: {}
|
||||||
|
|
||||||
|
type-fest@4.41.0: {}
|
||||||
|
|
||||||
type-is@1.6.18:
|
type-is@1.6.18:
|
||||||
dependencies:
|
dependencies:
|
||||||
media-typer: 0.3.0
|
media-typer: 0.3.0
|
||||||
@@ -6359,7 +6489,7 @@ snapshots:
|
|||||||
app-root-path: 3.1.0
|
app-root-path: 3.1.0
|
||||||
buffer: 6.0.3
|
buffer: 6.0.3
|
||||||
dayjs: 1.11.21
|
dayjs: 1.11.21
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
dedent: 1.7.2
|
dedent: 1.7.2
|
||||||
dotenv: 16.6.1
|
dotenv: 16.6.1
|
||||||
glob: 10.5.0
|
glob: 10.5.0
|
||||||
@@ -6407,6 +6537,8 @@ snapshots:
|
|||||||
escalade: 3.2.0
|
escalade: 3.2.0
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
|
|
||||||
|
uri-js-replace@1.0.1: {}
|
||||||
|
|
||||||
uri-js@4.4.1:
|
uri-js@4.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.1
|
punycode: 2.3.1
|
||||||
@@ -6425,7 +6557,7 @@ snapshots:
|
|||||||
vite-node@3.2.4(@types/node@22.20.0)(sass@1.102.0)(terser@5.50.0)(tsx@4.20.6):
|
vite-node@3.2.4(@types/node@22.20.0)(sass@1.102.0)(terser@5.50.0)(tsx@4.20.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
es-module-lexer: 1.7.0
|
es-module-lexer: 1.7.0
|
||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
vite: 6.4.3(@types/node@22.20.0)(sass@1.102.0)(terser@5.50.0)(tsx@4.20.6)
|
vite: 6.4.3(@types/node@22.20.0)(sass@1.102.0)(terser@5.50.0)(tsx@4.20.6)
|
||||||
@@ -6469,7 +6601,7 @@ snapshots:
|
|||||||
'@vitest/spy': 3.2.7
|
'@vitest/spy': 3.2.7
|
||||||
'@vitest/utils': 3.2.7
|
'@vitest/utils': 3.2.7
|
||||||
chai: 5.3.3
|
chai: 5.3.3
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
expect-type: 1.4.0
|
expect-type: 1.4.0
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
@@ -6511,7 +6643,7 @@ snapshots:
|
|||||||
|
|
||||||
vue-eslint-parser@9.4.3(eslint@9.39.5):
|
vue-eslint-parser@9.4.3(eslint@9.39.5):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.3
|
debug: 4.4.3(supports-color@10.2.2)
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5
|
||||||
eslint-scope: 7.2.2
|
eslint-scope: 7.2.2
|
||||||
eslint-visitor-keys: 3.4.3
|
eslint-visitor-keys: 3.4.3
|
||||||
@@ -6674,6 +6806,8 @@ snapshots:
|
|||||||
|
|
||||||
y18n@5.0.8: {}
|
y18n@5.0.8: {}
|
||||||
|
|
||||||
|
yaml-ast-parser@0.0.43: {}
|
||||||
|
|
||||||
yargs-parser@21.1.1: {}
|
yargs-parser@21.1.1: {}
|
||||||
|
|
||||||
yargs@17.7.3:
|
yargs@17.7.3:
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import { readdir, readFile, stat } from 'node:fs/promises';
|
||||||
|
import path from 'node:path';
|
||||||
|
import process from 'node:process';
|
||||||
|
import ts from 'typescript';
|
||||||
|
|
||||||
|
const TYPESCRIPT_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts']);
|
||||||
|
const INTERNAL_MESSAGE_MARKERS = ['MessageEnvelope', 'message-contracts'];
|
||||||
|
|
||||||
|
async function collectTypeScriptFiles(directory) {
|
||||||
|
const entries = await readdir(directory, { withFileTypes: true });
|
||||||
|
const nestedFiles = await Promise.all(
|
||||||
|
entries.map(async (entry) => {
|
||||||
|
const entryPath = path.join(directory, entry.name);
|
||||||
|
if (entry.isDirectory()) return collectTypeScriptFiles(entryPath);
|
||||||
|
return TYPESCRIPT_EXTENSIONS.has(path.extname(entry.name)) ? [entryPath] : [];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return nestedFiles.flat();
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectModuleSpecifiers(sourceFile) {
|
||||||
|
const specifiers = [];
|
||||||
|
|
||||||
|
function visit(node) {
|
||||||
|
if (
|
||||||
|
(ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&
|
||||||
|
node.moduleSpecifier &&
|
||||||
|
ts.isStringLiteralLike(node.moduleSpecifier)
|
||||||
|
) {
|
||||||
|
specifiers.push(node.moduleSpecifier.text);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
ts.isCallExpression(node) &&
|
||||||
|
node.expression.kind === ts.SyntaxKind.ImportKeyword &&
|
||||||
|
node.arguments.length === 1 &&
|
||||||
|
ts.isStringLiteralLike(node.arguments[0])
|
||||||
|
) {
|
||||||
|
specifiers.push(node.arguments[0].text);
|
||||||
|
}
|
||||||
|
ts.forEachChild(node, visit);
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(sourceFile);
|
||||||
|
return specifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolvesInsideServer(specifier, sourcePath, repositoryRoot, compilerOptions) {
|
||||||
|
const serverRoot = path.join(repositoryRoot, 'server');
|
||||||
|
const resolved = ts.resolveModuleName(specifier, sourcePath, compilerOptions, ts.sys)
|
||||||
|
.resolvedModule?.resolvedFileName;
|
||||||
|
|
||||||
|
// 模块解析在 fixture 没有目标文件时可能失败,此时仍需按导入者目录规范化相对路径,
|
||||||
|
// 避免通过尚未生成的 Server 文件绕过工程边界。
|
||||||
|
const candidate =
|
||||||
|
resolved ??
|
||||||
|
(specifier.startsWith('.') ? path.resolve(path.dirname(sourcePath), specifier) : '');
|
||||||
|
return candidate === serverRoot || candidate.startsWith(`${serverRoot}${path.sep}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verifyProjectBoundaries(repositoryRoot) {
|
||||||
|
const webSourceRoot = path.join(repositoryRoot, 'web', 'src');
|
||||||
|
const configPath = path.join(repositoryRoot, 'web', 'tsconfig.json');
|
||||||
|
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
|
||||||
|
const compilerOptions = configFile.error
|
||||||
|
? { module: ts.ModuleKind.ESNext, moduleResolution: ts.ModuleResolutionKind.Bundler }
|
||||||
|
: ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath)).options;
|
||||||
|
const files = await collectTypeScriptFiles(webSourceRoot);
|
||||||
|
const violations = [];
|
||||||
|
|
||||||
|
for (const sourcePath of files) {
|
||||||
|
const sourceText = await readFile(sourcePath, 'utf8');
|
||||||
|
const sourceFile = ts.createSourceFile(
|
||||||
|
sourcePath,
|
||||||
|
sourceText,
|
||||||
|
ts.ScriptTarget.Latest,
|
||||||
|
true,
|
||||||
|
ts.ScriptKind.TS,
|
||||||
|
);
|
||||||
|
for (const specifier of collectModuleSpecifiers(sourceFile)) {
|
||||||
|
if (resolvesInsideServer(specifier, sourcePath, repositoryRoot, compilerOptions)) {
|
||||||
|
violations.push(
|
||||||
|
`Web 禁止依赖 Server:${path.relative(repositoryRoot, sourcePath)} -> ${specifier}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const generatedSchemaPath = path.join(webSourceRoot, 'api', 'generated', 'schema.ts');
|
||||||
|
try {
|
||||||
|
const generatedSchema = await readFile(generatedSchemaPath, 'utf8');
|
||||||
|
for (const marker of INTERNAL_MESSAGE_MARKERS) {
|
||||||
|
if (generatedSchema.includes(marker)) {
|
||||||
|
violations.push(`生成契约包含 Server 内部消息:${marker}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code !== 'ENOENT') throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (violations.length > 0) {
|
||||||
|
throw new Error(violations.join('\n'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseRepositoryRoot(argv) {
|
||||||
|
const rootIndex = argv.indexOf('--root');
|
||||||
|
if (rootIndex >= 0 && argv[rootIndex + 1]) return path.resolve(argv[rootIndex + 1]);
|
||||||
|
return process.cwd();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
process.argv[1] &&
|
||||||
|
path.resolve(process.argv[1]) === path.resolve(new URL(import.meta.url).pathname)
|
||||||
|
) {
|
||||||
|
const repositoryRoot = parseRepositoryRoot(process.argv.slice(2));
|
||||||
|
try {
|
||||||
|
const rootStats = await stat(repositoryRoot);
|
||||||
|
if (!rootStats.isDirectory()) throw new Error('仓库根路径不是目录');
|
||||||
|
await verifyProjectBoundaries(repositoryRoot);
|
||||||
|
process.stdout.write('项目边界验证通过\n');
|
||||||
|
} catch (error) {
|
||||||
|
process.stderr.write(`${error.message}\n`);
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { execFile } from 'node:child_process';
|
||||||
|
import { mkdtemp, mkdir, writeFile } from 'node:fs/promises';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import path from 'node:path';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { promisify } from 'node:util';
|
||||||
|
|
||||||
|
const execFileAsync = promisify(execFile);
|
||||||
|
const checkerPath = path.resolve('tools/boundaries/verify-project-boundaries.mjs');
|
||||||
|
|
||||||
|
async function createFixture() {
|
||||||
|
const repositoryRoot = await mkdtemp(path.join(tmpdir(), 'a-share-boundary-'));
|
||||||
|
await mkdir(path.join(repositoryRoot, 'web/src/api/generated'), { recursive: true });
|
||||||
|
await mkdir(path.join(repositoryRoot, 'server/libs'), { recursive: true });
|
||||||
|
await writeFile(
|
||||||
|
path.join(repositoryRoot, 'web/src/api/generated/schema.ts'),
|
||||||
|
'export interface paths {}\n',
|
||||||
|
);
|
||||||
|
return repositoryRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runChecker(repositoryRoot) {
|
||||||
|
try {
|
||||||
|
await execFileAsync(process.execPath, [checkerPath, '--root', repositoryRoot]);
|
||||||
|
return { exitCode: 0, stderr: '' };
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
exitCode: error.code,
|
||||||
|
stderr: String(error.stderr),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test('拒绝 Web 通过静态、动态或导出语句越界依赖 Server', async () => {
|
||||||
|
const repositoryRoot = await createFixture();
|
||||||
|
await writeFile(
|
||||||
|
path.join(repositoryRoot, 'web/src/illegal.ts'),
|
||||||
|
[
|
||||||
|
"import '../../server/libs/static';",
|
||||||
|
"export { value } from '../../server/libs/exported';",
|
||||||
|
"void import('../../server/libs/dynamic');",
|
||||||
|
].join('\n'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await runChecker(repositoryRoot);
|
||||||
|
|
||||||
|
assert.equal(result.exitCode, 1);
|
||||||
|
assert.match(result.stderr, /Web 禁止依赖 Server/);
|
||||||
|
assert.match(result.stderr, /illegal\.ts/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('拒绝生成契约泄漏 RocketMQ 内部消息结构', async () => {
|
||||||
|
const repositoryRoot = await createFixture();
|
||||||
|
await writeFile(
|
||||||
|
path.join(repositoryRoot, 'web/src/api/generated/schema.ts'),
|
||||||
|
'export interface MessageEnvelope { messageId: string }\n',
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await runChecker(repositoryRoot);
|
||||||
|
|
||||||
|
assert.equal(result.exitCode, 1);
|
||||||
|
assert.match(result.stderr, /生成契约包含 Server 内部消息/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('拒绝通过 tsconfig 路径别名依赖 Server', async () => {
|
||||||
|
const repositoryRoot = await createFixture();
|
||||||
|
await writeFile(
|
||||||
|
path.join(repositoryRoot, 'web/tsconfig.json'),
|
||||||
|
JSON.stringify({
|
||||||
|
compilerOptions: {
|
||||||
|
baseUrl: '.',
|
||||||
|
paths: { '@server/*': ['../server/*'] },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
await writeFile(
|
||||||
|
path.join(repositoryRoot, 'web/src/aliased.ts'),
|
||||||
|
"import type { Internal } from '@server/libs/internal';\nexport type Leaked = Internal;\n",
|
||||||
|
);
|
||||||
|
await writeFile(
|
||||||
|
path.join(repositoryRoot, 'server/libs/internal.ts'),
|
||||||
|
'export interface Internal { secret: string }\n',
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await runChecker(repositoryRoot);
|
||||||
|
|
||||||
|
assert.equal(result.exitCode, 1);
|
||||||
|
assert.match(result.stderr, /Web 禁止依赖 Server/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('接受只消费公开 OpenAPI 类型的 Web 工程', async () => {
|
||||||
|
const repositoryRoot = await createFixture();
|
||||||
|
await writeFile(
|
||||||
|
path.join(repositoryRoot, 'web/src/legal.ts'),
|
||||||
|
"import type { paths } from './api/generated/schema';\nexport type ApiPaths = paths;\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await runChecker(repositoryRoot);
|
||||||
|
|
||||||
|
assert.equal(result.exitCode, 0);
|
||||||
|
assert.equal(result.stderr, '');
|
||||||
|
});
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host 127.0.0.1",
|
"dev": "vite --host 127.0.0.1",
|
||||||
|
"api:generate": "openapi-typescript ../server/openapi/openapi.json -o src/api/generated/schema.ts",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"typecheck": "vue-tsc --noEmit && tsc --noEmit -p tsconfig.node.json",
|
"typecheck": "vue-tsc --noEmit && tsc --noEmit -p tsconfig.node.json",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"naive-ui": "2.44.1",
|
"naive-ui": "2.44.1",
|
||||||
|
"openapi-fetch": "0.17.0",
|
||||||
"pinia": "3.0.4",
|
"pinia": "3.0.4",
|
||||||
"vue": "3.5.41",
|
"vue": "3.5.41",
|
||||||
"vue-router": "4.6.4"
|
"vue-router": "4.6.4"
|
||||||
@@ -26,6 +28,7 @@
|
|||||||
"dotenv": "16.6.1",
|
"dotenv": "16.6.1",
|
||||||
"jsdom": "26.1.0",
|
"jsdom": "26.1.0",
|
||||||
"katex": "0.18.4",
|
"katex": "0.18.4",
|
||||||
|
"openapi-typescript": "7.13.0",
|
||||||
"sass": "1.102.0",
|
"sass": "1.102.0",
|
||||||
"typescript": "5.9.3",
|
"typescript": "5.9.3",
|
||||||
"vite": "6.4.3",
|
"vite": "6.4.3",
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import type { components } from '../generated/schema';
|
||||||
|
import { toHealthViewModel } from './health.adapter';
|
||||||
|
|
||||||
|
type HealthResponseDto = components['schemas']['HealthResponseDto'];
|
||||||
|
|
||||||
|
const healthyDto: HealthResponseDto = {
|
||||||
|
status: 'healthy',
|
||||||
|
process: 'api',
|
||||||
|
checkedAt: '2026-08-13T09:30:00.000Z',
|
||||||
|
dependencies: {
|
||||||
|
mysql: { status: 'healthy', detail: 'MySQL 5.7 ready' },
|
||||||
|
redis: { status: 'healthy', detail: 'PONG' },
|
||||||
|
rocketMq: { status: 'healthy', detail: 'RocketMQ ready' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('健康响应页面适配器', () => {
|
||||||
|
it('把全部可用的传输状态映射为健康页面模型', () => {
|
||||||
|
expect(toHealthViewModel(healthyDto)).toEqual({
|
||||||
|
state: 'healthy',
|
||||||
|
stateLabel: '运行正常',
|
||||||
|
checkedAt: new Date('2026-08-13T09:30:00.000Z'),
|
||||||
|
services: [
|
||||||
|
{ key: 'redis', name: 'Redis', available: true, note: 'PONG' },
|
||||||
|
{ key: 'mysql', name: 'MySQL', available: true, note: 'MySQL 5.7 ready' },
|
||||||
|
{ key: 'rocketMq', name: 'RocketMQ', available: true, note: 'RocketMQ ready' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('把可选能力停用映射为降级而不是后端枚举', () => {
|
||||||
|
const dto: HealthResponseDto = {
|
||||||
|
...healthyDto,
|
||||||
|
dependencies: {
|
||||||
|
...healthyDto.dependencies,
|
||||||
|
rocketMq: { status: 'disabled', detail: '当前版本未启用' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewModel = toHealthViewModel(dto);
|
||||||
|
|
||||||
|
expect(viewModel.state).toBe('degraded');
|
||||||
|
expect(viewModel.stateLabel).toBe('部分能力停用');
|
||||||
|
expect(viewModel.services[2]).toEqual({
|
||||||
|
key: 'rocketMq',
|
||||||
|
name: 'RocketMQ',
|
||||||
|
available: false,
|
||||||
|
note: '当前版本未启用',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('把任一依赖不可用映射为不可用', () => {
|
||||||
|
const dto: HealthResponseDto = {
|
||||||
|
...healthyDto,
|
||||||
|
status: 'unavailable',
|
||||||
|
dependencies: {
|
||||||
|
...healthyDto.dependencies,
|
||||||
|
redis: { status: 'unavailable', detail: '检查失败' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(toHealthViewModel(dto).state).toBe('unavailable');
|
||||||
|
expect(toHealthViewModel(dto).stateLabel).toBe('服务异常');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('拒绝运行时出现的未知依赖状态', () => {
|
||||||
|
const malformedDto = structuredClone(healthyDto) as unknown as {
|
||||||
|
dependencies: { redis: { status: string } };
|
||||||
|
};
|
||||||
|
malformedDto.dependencies.redis.status = 'unknown';
|
||||||
|
|
||||||
|
expect(() => toHealthViewModel(malformedDto as never)).toThrow(/未知依赖状态/);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import type { components } from '../generated/schema';
|
||||||
|
|
||||||
|
type HealthResponseDto = components['schemas']['HealthResponseDto'];
|
||||||
|
type DependencyHealthDto = components['schemas']['DependencyHealthDto'];
|
||||||
|
|
||||||
|
export type HealthViewState = 'healthy' | 'degraded' | 'unavailable';
|
||||||
|
|
||||||
|
export interface HealthServiceViewModel {
|
||||||
|
key: 'redis' | 'mysql' | 'rocketMq';
|
||||||
|
name: 'Redis' | 'MySQL' | 'RocketMQ';
|
||||||
|
available: boolean;
|
||||||
|
note: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HealthViewModel {
|
||||||
|
state: HealthViewState;
|
||||||
|
stateLabel: '运行正常' | '部分能力停用' | '服务异常';
|
||||||
|
checkedAt: Date;
|
||||||
|
services: HealthServiceViewModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertDependencyStatus(status: string): asserts status is DependencyHealthDto['status'] {
|
||||||
|
if (status !== 'healthy' && status !== 'unavailable' && status !== 'disabled') {
|
||||||
|
throw new Error(`未知依赖状态:${status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toServiceViewModel(
|
||||||
|
key: HealthServiceViewModel['key'],
|
||||||
|
name: HealthServiceViewModel['name'],
|
||||||
|
dependency: DependencyHealthDto,
|
||||||
|
): HealthServiceViewModel {
|
||||||
|
assertDependencyStatus(dependency.status);
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
name,
|
||||||
|
available: dependency.status === 'healthy',
|
||||||
|
note: dependency.detail,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toHealthViewModel(dto: HealthResponseDto): HealthViewModel {
|
||||||
|
const dependencies = Object.values(dto.dependencies);
|
||||||
|
for (const dependency of dependencies) assertDependencyStatus(dependency.status);
|
||||||
|
|
||||||
|
const hasUnavailable = dependencies.some((dependency) => dependency.status === 'unavailable');
|
||||||
|
const hasDisabled = dependencies.some((dependency) => dependency.status === 'disabled');
|
||||||
|
const state: HealthViewState =
|
||||||
|
dto.status === 'unavailable' || hasUnavailable
|
||||||
|
? 'unavailable'
|
||||||
|
: hasDisabled
|
||||||
|
? 'degraded'
|
||||||
|
: 'healthy';
|
||||||
|
|
||||||
|
const stateLabel = {
|
||||||
|
healthy: '运行正常',
|
||||||
|
degraded: '部分能力停用',
|
||||||
|
unavailable: '服务异常',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 页面只消费稳定的展示模型,后端枚举和依赖对象结构不会直接扩散到 Vue 组件。
|
||||||
|
return {
|
||||||
|
state,
|
||||||
|
stateLabel: stateLabel[state],
|
||||||
|
checkedAt: new Date(dto.checkedAt),
|
||||||
|
services: [
|
||||||
|
toServiceViewModel('redis', 'Redis', dto.dependencies.redis),
|
||||||
|
toServiceViewModel('mysql', 'MySQL', dto.dependencies.mysql),
|
||||||
|
toServiceViewModel('rocketMq', 'RocketMQ', dto.dependencies.rocketMq),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { ApiRequestError, createApiClient } from './client';
|
||||||
|
|
||||||
|
describe('OpenAPI 请求客户端', () => {
|
||||||
|
it('使用调用方提供的 API 根地址请求公开路径', async () => {
|
||||||
|
let requestedUrl = '';
|
||||||
|
const client = createApiClient('http://127.0.0.1:3000', async (request) => {
|
||||||
|
requestedUrl = request.url;
|
||||||
|
return Response.json({
|
||||||
|
status: 'healthy',
|
||||||
|
process: 'api',
|
||||||
|
checkedAt: '2026-08-13T09:30:00.000Z',
|
||||||
|
dependencies: {
|
||||||
|
mysql: { status: 'healthy', detail: 'ready' },
|
||||||
|
redis: { status: 'healthy', detail: 'ready' },
|
||||||
|
rocketMq: { status: 'disabled', detail: 'disabled' },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await client.GET('/api/v1/health');
|
||||||
|
|
||||||
|
expect(requestedUrl).toBe('http://127.0.0.1:3000/api/v1/health');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('把网络错误归一化为不泄漏底层连接信息的错误', async () => {
|
||||||
|
const client = createApiClient('http://127.0.0.1:3000', async () => {
|
||||||
|
throw new Error('connect ECONNREFUSED mysql://root:secret@internal');
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(client.GET('/api/v1/health')).rejects.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
name: 'ApiRequestError',
|
||||||
|
message: 'API 请求失败,请稍后重试',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
await expect(client.GET('/api/v1/health')).rejects.toBeInstanceOf(ApiRequestError);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import createClient, { type Client } from 'openapi-fetch';
|
||||||
|
import type { paths } from './generated/schema';
|
||||||
|
|
||||||
|
export class ApiRequestError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super('API 请求失败,请稍后重试');
|
||||||
|
this.name = 'ApiRequestError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createApiClient(
|
||||||
|
baseUrl: string,
|
||||||
|
fetchImplementation: (request: Request) => Promise<Response> = globalThis.fetch,
|
||||||
|
): Client<paths> {
|
||||||
|
const client = createClient<paths>({
|
||||||
|
baseUrl: baseUrl.replace(/\/$/, ''),
|
||||||
|
fetch: fetchImplementation,
|
||||||
|
});
|
||||||
|
|
||||||
|
client.use({
|
||||||
|
onError() {
|
||||||
|
// 浏览器错误可能带出内网主机、代理或底层连接细节,统一转换为安全的用户提示。
|
||||||
|
return new ApiRequestError();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* This file was auto-generated by openapi-typescript.
|
||||||
|
* Do not make direct changes to the file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface paths {
|
||||||
|
'/api/v1/health': {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get: operations['HealthController_getHealth'];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export type webhooks = Record<string, never>;
|
||||||
|
export interface components {
|
||||||
|
schemas: {
|
||||||
|
DependencyHealthDto: {
|
||||||
|
detail: string;
|
||||||
|
/** @enum {string} */
|
||||||
|
status: 'healthy' | 'unavailable' | 'disabled';
|
||||||
|
};
|
||||||
|
HealthDependenciesDto: {
|
||||||
|
mysql: components['schemas']['DependencyHealthDto'];
|
||||||
|
redis: components['schemas']['DependencyHealthDto'];
|
||||||
|
rocketMq: components['schemas']['DependencyHealthDto'];
|
||||||
|
};
|
||||||
|
HealthResponseDto: {
|
||||||
|
/** Format: date-time */
|
||||||
|
checkedAt: string;
|
||||||
|
dependencies: components['schemas']['HealthDependenciesDto'];
|
||||||
|
/** @enum {string} */
|
||||||
|
process: 'api';
|
||||||
|
/** @enum {string} */
|
||||||
|
status: 'healthy' | 'unavailable';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: never;
|
||||||
|
parameters: never;
|
||||||
|
requestBodies: never;
|
||||||
|
headers: never;
|
||||||
|
pathItems: never;
|
||||||
|
}
|
||||||
|
export type $defs = Record<string, never>;
|
||||||
|
export interface operations {
|
||||||
|
HealthController_getHealth: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['HealthResponseDto'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
503: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['HealthResponseDto'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user