Compare commits
1 Commits
release/1.
...
release/1.
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e7b2ec0ba |
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"moduleCode": "order",
|
"moduleCode": "order",
|
||||||
"candidateVersion": "1.0.0-rc.1",
|
"candidateVersion": "1.0.1-rc.2",
|
||||||
"buildRevision": 1,
|
"buildRevision": 2,
|
||||||
"buildChecksum": "5cf032fdac2b0403fbcad8b44b326a29141ba556bbbb9710ac9329b5d7c031dd",
|
"buildChecksum": "e71d92cc440437054c77fef0dbee098c6ceb8700d9a7d828dcad86420f230f22",
|
||||||
"schemaInputChecksum": "1b3eec01e5b04880166e20e8e6df70b79f227129ea9165e38cfbb925be3c2d23",
|
"schemaInputChecksum": "34ed49d330f45bd9933b582bc9265cf0f4e2e2683833d0f39e38a357d138ab65",
|
||||||
"branchName": "release/1.0.0-rc.1"
|
"branchName": "release/1.0.1-rc.2"
|
||||||
}
|
}
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -9,7 +9,3 @@ coverage/
|
|||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
.tmp-*
|
.tmp-*
|
||||||
!pnpm-lock.yaml
|
|
||||||
|
|
||||||
# Local candidate archives are superseded by the private registry
|
|
||||||
vendor/
|
|
||||||
|
|||||||
5
bootstrap.js
vendored
5
bootstrap.js
vendored
@@ -11,10 +11,11 @@ function loadGeneratedSaasModule() {
|
|||||||
return require('./dist/saas/generated').GeneratedSaasModule;
|
return require('./dist/saas/generated').GeneratedSaasModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function start() {
|
async function start(options = {}) {
|
||||||
await prepareSaasServiceHost({
|
const session = await prepareSaasServiceHost({
|
||||||
generated: loadGeneratedSaasModule(),
|
generated: loadGeneratedSaasModule(),
|
||||||
});
|
});
|
||||||
|
if (options.verifySession) await options.verifySession(session);
|
||||||
requireExactReleaseEnvironment();
|
requireExactReleaseEnvironment();
|
||||||
return Bootstrap.configure({
|
return Bootstrap.configure({
|
||||||
baseDir: path.join(__dirname, 'dist'),
|
baseDir: path.join(__dirname, 'dist'),
|
||||||
|
|||||||
16
package.json
16
package.json
@@ -10,19 +10,21 @@
|
|||||||
"build": "rimraf dist && mwtsc --cleanOutDir",
|
"build": "rimraf dist && mwtsc --cleanOutDir",
|
||||||
"publish": "cool saas-model finalize",
|
"publish": "cool saas-model finalize",
|
||||||
"start": "node bootstrap.js",
|
"start": "node bootstrap.js",
|
||||||
"start:local": "cross-env NODE_ENV=local SAAS_RUNTIME_BOOTSTRAP_LOCAL_FALLBACK_ENABLED=true node bootstrap.js",
|
"start:stable": "node start-stable.js",
|
||||||
"verify:packages": "node scripts/verify-local-packages.cjs"
|
"test:stable": "node --test test/start-stable.test.cjs",
|
||||||
|
"start:local": "cross-env NODE_ENV=local SAAS_RUNTIME_BOOTSTRAP_LOCAL_FALLBACK_ENABLED=true node bootstrap.js"
|
||||||
},
|
},
|
||||||
"coolSaas": {
|
"coolSaas": {
|
||||||
"lifecycle": {
|
"lifecycle": {
|
||||||
"dev": "pnpm run build && pnpm run start:local",
|
"dev": "pnpm run build && pnpm run start:local",
|
||||||
"test": "pnpm run typecheck",
|
"test": "pnpm run typecheck && pnpm run test:stable",
|
||||||
"build": "pnpm run build"
|
"build": "pnpm run build",
|
||||||
|
"artifactDir": "dist"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cool-midway/module-runtime": "9.0.3",
|
"@cool-midway/module-runtime": "9.0.2",
|
||||||
"@cool-midway/rpc": "9.0.3",
|
"@cool-midway/rpc": "9.0.2",
|
||||||
"@midwayjs/bootstrap": "^3.20.3",
|
"@midwayjs/bootstrap": "^3.20.3",
|
||||||
"@midwayjs/core": "^3.20.3",
|
"@midwayjs/core": "^3.20.3",
|
||||||
"@midwayjs/koa": "^3.20.3",
|
"@midwayjs/koa": "^3.20.3",
|
||||||
@@ -33,7 +35,7 @@
|
|||||||
"typeorm": "npm:@cool-midway/typeorm@0.3.20"
|
"typeorm": "npm:@cool-midway/typeorm@0.3.20"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cool-midway/core": "8.0.12",
|
"@cool-midway/core": "8.0.11",
|
||||||
"@types/node": "22.10.7",
|
"@types/node": "22.10.7",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"mwtsc": "^1.16.0",
|
"mwtsc": "^1.16.0",
|
||||||
|
|||||||
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
@@ -9,11 +9,11 @@ importers:
|
|||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@cool-midway/module-runtime':
|
'@cool-midway/module-runtime':
|
||||||
specifier: 9.0.3
|
specifier: 9.0.2
|
||||||
version: 9.0.3(@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7)))(@midwayjs/core@3.20.24)
|
version: 9.0.2(@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7)))(@midwayjs/core@3.20.24)
|
||||||
'@cool-midway/rpc':
|
'@cool-midway/rpc':
|
||||||
specifier: 9.0.3
|
specifier: 9.0.2
|
||||||
version: 9.0.3(@cool-midway/module-runtime@9.0.3(@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7)))(@midwayjs/core@3.20.24))(@midwayjs/validate@3.20.24)(debug@4.4.3)
|
version: 9.0.2(@cool-midway/module-runtime@9.0.2(@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7)))(@midwayjs/core@3.20.24))(@midwayjs/validate@3.20.24)(debug@4.4.3)
|
||||||
'@midwayjs/bootstrap':
|
'@midwayjs/bootstrap':
|
||||||
specifier: ^3.20.3
|
specifier: ^3.20.3
|
||||||
version: 3.20.24
|
version: 3.20.24
|
||||||
@@ -40,8 +40,8 @@ importers:
|
|||||||
version: '@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7))'
|
version: '@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7))'
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@cool-midway/core':
|
'@cool-midway/core':
|
||||||
specifier: 8.0.12
|
specifier: 8.0.11
|
||||||
version: 8.0.12(debug@4.4.3)
|
version: 8.0.11(debug@4.4.3)
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: 22.10.7
|
specifier: 22.10.7
|
||||||
version: 22.10.7
|
version: 22.10.7
|
||||||
@@ -68,20 +68,20 @@ packages:
|
|||||||
resolution: {integrity: sha512-Bl43bVCUm0V+w1wkfOicRfiHHZtaH1mQCp06baNapG86uC7OiXwMrml5+cvZD+mYSHdP07qUxtqkL8oUhrgAfg==}
|
resolution: {integrity: sha512-Bl43bVCUm0V+w1wkfOicRfiHHZtaH1mQCp06baNapG86uC7OiXwMrml5+cvZD+mYSHdP07qUxtqkL8oUhrgAfg==}
|
||||||
engines: {node: '>=8.0.0'}
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
'@cool-midway/core@8.0.12':
|
'@cool-midway/core@8.0.11':
|
||||||
resolution: {integrity: sha512-PZZ50p2xjtU2AoaOjrDCh/cigDHvxsXvD6NzA80F74D1JEc6zbWzA/uJSW5AG7Vgqz6bOlA3C4knwk6czdlz2w==}
|
resolution: {integrity: sha512-fcx4A67Nqjh7N4Ujc975ZPHvkHP+BGLSjcFXcmlozSBJvuxR+ftBh1zmoFWsJ3o35YUqKGC5eA4v9i2vV6kw/A==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@cool-midway/module-runtime@9.0.3':
|
'@cool-midway/module-runtime@9.0.2':
|
||||||
resolution: {integrity: sha512-hHczZpeZQBec6HeMCet4pXeAz3FzlNlUPeE7SnrexItatF9xoiKuT5KfcccXcxBSYmYGmkpnuEnntHM+//freg==}
|
resolution: {integrity: sha512-eYB57p/wshDUSaqTmZHsh5LGDpPgQaii4jPG5vORcdgoCxxcGuRu9fhcTnuRhlfTYj+Gh71Oa1QgoRXdFE7tRA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@midwayjs/core': '>=3.20.0'
|
'@midwayjs/core': '>=3.20.0'
|
||||||
typeorm: '>=0.3.20'
|
typeorm: '>=0.3.20'
|
||||||
|
|
||||||
'@cool-midway/rpc@9.0.3':
|
'@cool-midway/rpc@9.0.2':
|
||||||
resolution: {integrity: sha512-Sxks1b2Vzvo08SlupHM3QUnQay6vXL8eibWIzpzm7mzMJONLpibr7mKT1KFgGxeXaG3pcCWl0j1D9wpwRS233w==}
|
resolution: {integrity: sha512-GBxjGslo538m2v3Z2AeuKmtj8JMjAAIbnzqwSqYmjz9C/sQZ9V6trWgu5TcQIAnXVAGDY1aVcK8pVg16T6LRDg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@cool-midway/module-runtime': '>=9.0.3 <10'
|
'@cool-midway/module-runtime': '>=9.0.0'
|
||||||
'@midwayjs/validate': '>=3.20.0'
|
'@midwayjs/validate': '>=3.20.0'
|
||||||
|
|
||||||
'@cool-midway/typeorm@0.3.20':
|
'@cool-midway/typeorm@0.3.20':
|
||||||
@@ -2469,7 +2469,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
lockfile: 1.0.4
|
lockfile: 1.0.4
|
||||||
|
|
||||||
'@cool-midway/core@8.0.12(debug@4.4.3)':
|
'@cool-midway/core@8.0.11(debug@4.4.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@cool-midway/cache-manager-fs-hash': 7.0.0
|
'@cool-midway/cache-manager-fs-hash': 7.0.0
|
||||||
'@midwayjs/cache-manager': 3.20.24
|
'@midwayjs/cache-manager': 3.20.24
|
||||||
@@ -2492,14 +2492,14 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
|
||||||
'@cool-midway/module-runtime@9.0.3(@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7)))(@midwayjs/core@3.20.24)':
|
'@cool-midway/module-runtime@9.0.2(@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7)))(@midwayjs/core@3.20.24)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@midwayjs/core': 3.20.24
|
'@midwayjs/core': 3.20.24
|
||||||
typeorm: '@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7))'
|
typeorm: '@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7))'
|
||||||
|
|
||||||
'@cool-midway/rpc@9.0.3(@cool-midway/module-runtime@9.0.3(@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7)))(@midwayjs/core@3.20.24))(@midwayjs/validate@3.20.24)(debug@4.4.3)':
|
'@cool-midway/rpc@9.0.2(@cool-midway/module-runtime@9.0.2(@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7)))(@midwayjs/core@3.20.24))(@midwayjs/validate@3.20.24)(debug@4.4.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@cool-midway/module-runtime': 9.0.3(@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7)))(@midwayjs/core@3.20.24)
|
'@cool-midway/module-runtime': 9.0.2(@cool-midway/typeorm@0.3.20(ioredis@5.11.1)(mysql2@3.24.3(@types/node@22.10.7)))(@midwayjs/core@3.20.24)
|
||||||
'@midwayjs/validate': 3.20.24
|
'@midwayjs/validate': 3.20.24
|
||||||
ioredis: 5.11.1
|
ioredis: 5.11.1
|
||||||
moleculer: 0.14.36(debug@4.4.3)(ioredis@5.11.1)
|
moleculer: 0.14.36(debug@4.4.3)(ioredis@5.11.1)
|
||||||
|
|||||||
@@ -1,259 +0,0 @@
|
|||||||
{
|
|
||||||
"formatVersion": 1,
|
|
||||||
"packages": [
|
|
||||||
{
|
|
||||||
"name": "@cool-midway/core",
|
|
||||||
"version": "8.0.12",
|
|
||||||
"registry": "https://npm.blendercg.art/",
|
|
||||||
"tarball": "https://npm.blendercg.art/@cool-midway/core/-/core-8.0.12.tgz",
|
|
||||||
"integrity": "sha512-PZZ50p2xjtU2AoaOjrDCh/cigDHvxsXvD6NzA80F74D1JEc6zbWzA/uJSW5AG7Vgqz6bOlA3C4knwk6czdlz2w==",
|
|
||||||
"sha256": "d50a0ce219e4b31d1682653fb6f671c583d6e684ae9e1cad16a149105bc52257",
|
|
||||||
"files": {
|
|
||||||
"dist/bin/check.d.ts": "ad0ad50e02705414d917e4d7a6ee5140eebe8155312252299d944e246605ce05",
|
|
||||||
"dist/bin/check.js": "ae79d40c58bea4864ad914d148870b8bf9cc9d2bc40c2ef3408b91089915fcc9",
|
|
||||||
"dist/bin/entity.d.ts": "762d7c22631edf51f93fb3f587102fbd7c6c88b1f73fa2511cbf262a6ece1cb4",
|
|
||||||
"dist/bin/entity.js": "91dcc9e31bc16e6b7bc2824f1aede47c88b441d5ca5c453df423660b33f5e1ad",
|
|
||||||
"dist/bin/index.d.ts": "43e818adf60173644896298637f47b01d5819b17eda46eaa32d0c7d64724d012",
|
|
||||||
"dist/bin/index.js": "b36efbcab0f16b7ce886041f16840ad0fb98dfb89e0b3341c2c8aa2dcc1bd6df",
|
|
||||||
"dist/bin/obfuscate.d.ts": "685b95a0e000d3a796d60d31cb1766d52d00f98a25e9455158b5ff58d962cd4f",
|
|
||||||
"dist/bin/obfuscate.js": "803798e672a537c54e167837de34499592d0f5c991273a6f05515770357fa6ec",
|
|
||||||
"dist/bin/saas-model.d.ts": "af7900a770d59df795cd37f79839da84f92aa088503c3135348bdee632f1673a",
|
|
||||||
"dist/bin/saas-model.js": "483220e19f10f172bada3d9bf5157e070ca1029a85b568ec2e5ffcb3f9d037f4",
|
|
||||||
"dist/cache/store.d.ts": "a0adf1432d64d871ed02adcf0a9665b30143fba768326ff4e791ea51e9db8880",
|
|
||||||
"dist/cache/store.js": "3281e7851cd86dce35ab4db9b1915e00e11ff72cea7d1113b27721f0a547f04c",
|
|
||||||
"dist/config/config.default.d.ts": "2ad1c815b50f99f6adde59633395afddef5a323205c6387e76bd34bff8ca9c10",
|
|
||||||
"dist/config/config.default.js": "6ca05c58aff5f8ed6f6a340b27cc131587634e642c1bf72a97314f415f4f133d",
|
|
||||||
"dist/configuration.d.ts": "687f08e182e41372c41d257bf357ca03de2d44a623d7320d81dda1f37dc12505",
|
|
||||||
"dist/configuration.js": "51020f70b9f92cf07ad9c32b228450dc535a970b7de8863711ed920bfc003bae",
|
|
||||||
"dist/constant/global.d.ts": "22868dfe8944c356ea28b5d7cc08926b751df5d46abcb4a90410e01c3d5bd543",
|
|
||||||
"dist/constant/global.js": "e64496eead711739e85900659e72587115d02c52a1a10904758b632c4663345f",
|
|
||||||
"dist/controller/base.d.ts": "011b63eec356a69520b6a733f0dc5b2280f302c91f9c3c04fdc77b541340ab04",
|
|
||||||
"dist/controller/base.js": "c68dcb2ee7fe99389039333a5618cc3dfaf0ac87365b24b1bb443b670e81a46d",
|
|
||||||
"dist/decorator/cache.d.ts": "0f1df2f901d7c1015bde4d294a6a5a2dbbfe0e6d613478fc50e07144b26da0cc",
|
|
||||||
"dist/decorator/cache.js": "524b850c621e2efe30a54243857ffeeaab1ca14c2976ca9eb4e605d9dc61a729",
|
|
||||||
"dist/decorator/controller.d.ts": "0de521ff44f02336bf877f3952c56b3bf2ff4b0f0f7ae4787080d05f49ccaf34",
|
|
||||||
"dist/decorator/controller.js": "d3633ec4d10ea4f871f17ffdbcd91d58eda1fbd5f2191c8fcb5ff6738d4efb86",
|
|
||||||
"dist/decorator/event.d.ts": "f3c872d94fd6678719a2849f53b7747d6ac956d4e2a9a8df8fc488991c07bed3",
|
|
||||||
"dist/decorator/event.js": "69c91edf56c479fd36fbe27cdf4a5f215cb44efbe6aa7539bf361c0d2e239b20",
|
|
||||||
"dist/decorator/index.d.ts": "f53a6c43fd0d0f5d3b5d78d889ec62b3fc1909bf247c2eb99b3831c137092ac6",
|
|
||||||
"dist/decorator/index.js": "f93c258081679f1e5d11517bc29bcc6ca066749b1813d72da927ded484a51550",
|
|
||||||
"dist/decorator/tag.d.ts": "40fb36914821cf54a8d95ec54589af2d332b2a3c1dcf1f3e72b5c62b437220df",
|
|
||||||
"dist/decorator/tag.js": "9db13ad2fefc16675b87545fd994297c0250975e3d4ab057111e95554068089b",
|
|
||||||
"dist/decorator/transaction.d.ts": "7675127009885859307b9389bcf2bd28775005495a3eff4e86ddb9410fb999a1",
|
|
||||||
"dist/decorator/transaction.js": "39bef98a66b7a2c28af8c820671a0962ae9d12442a5bac67f0613e13a8ee91d1",
|
|
||||||
"dist/entity/base.d.ts": "50960ab93ad375f7e8296b3f8263d7def0c7af3ae4b7d35cc55612fb38fec9c8",
|
|
||||||
"dist/entity/base.js": "8363fa0ab183aae1cce4555070bb8b7cd454274fe873e80467371f54ea5c47af",
|
|
||||||
"dist/entity/mongo.d.ts": "6379516a8b1da880a11e0734ecb7b2e66728977c320386c9f73e1a926d6edee3",
|
|
||||||
"dist/entity/mongo.js": "73a13671d213521aa59ffadf10b072ec1ce3c9c1ad72f06bd30d1bd22beb8965",
|
|
||||||
"dist/entity/typeorm.d.ts": "78ca457d1f1a9e47efdfcb1b140d27019677c776b8ded7e8fc43127c9a844127",
|
|
||||||
"dist/entity/typeorm.js": "e1829a29ff62787a3fa7c206f6695098e260dd94363619dcf9800c3ab61a6f75",
|
|
||||||
"dist/event/index.d.ts": "5e14df52a58984680890b13d471044cf71b2ce50a0a8e157088297696e09692e",
|
|
||||||
"dist/event/index.js": "d6e1fd1e0e4949cad7299d4b332cc96549fd4174e20b1b85a44d1a6eabd6a31a",
|
|
||||||
"dist/exception/base.d.ts": "3e24d6c029e0f29a863b24b7d0f0509c7b0a3fbc74ee1aba4eb486ecd127e6ed",
|
|
||||||
"dist/exception/base.js": "d9e1f1cd65b051cfa2c5c67b58bde507da71986cedc35ee9a3f4b6e702ad1522",
|
|
||||||
"dist/exception/comm.d.ts": "0bf544d6a3c0712c94858eb14e82baa5e2f9890d65963a4082ea726b58413fcd",
|
|
||||||
"dist/exception/comm.js": "d747a79a35c8a81faaba46426e36612fd3b7092c4019be086d32d41048f6be42",
|
|
||||||
"dist/exception/core.d.ts": "50948b0bc83b5ba3bebab64c554a5cd931cf2c0deeaa5b4d320b2634f6e374ed",
|
|
||||||
"dist/exception/core.js": "10cd2c6ef01ed4e139a56aa9947cc536a3360e2d4662452bd91dbb91078b1d31",
|
|
||||||
"dist/exception/filter.d.ts": "6d78a77bd64fcb039991a885215e7d02fcd5dad1b18ef5ff593d6985cdb39d18",
|
|
||||||
"dist/exception/filter.js": "9c4eefb65168979ea7ae4b609407789ce3cc8f3a7f2d7baa8541dda0911c3195",
|
|
||||||
"dist/exception/validate.d.ts": "d34062b220d8cdfd38aabc595c935c7bcc35182c1ad7ff549b49a282a52473c5",
|
|
||||||
"dist/exception/validate.js": "be57f7f715f714dde594013860fc14a18db5115ad8be754ecfb8532d4d8440d6",
|
|
||||||
"dist/index.d.ts": "a260cd478d8f6ef04880a0a9cdb412599596a928ef7ac5f36920a5b2adce3927",
|
|
||||||
"dist/index.js": "6a5d80cb2841d17c810d872ff412c60de617227971a84e923b5994e8ec334906",
|
|
||||||
"dist/interface.d.ts": "71596c601080e12af569f82134e8c4a9dec24710ec9a9a33f32d7c6b79aeb80e",
|
|
||||||
"dist/interface.js": "7e811aba01a1704883608710e99deabe017128fdb69a9b760e541c9f3fd6f138",
|
|
||||||
"dist/module/config.d.ts": "10c5151ac83da5bf21c0319dcb9cee9091a1624633688e2310f10fcda0ccb1eb",
|
|
||||||
"dist/module/config.js": "d043cc225850b2d4785c8dbf06646dca08a0d7ed85e55931460ed0bb6cb0c860",
|
|
||||||
"dist/module/import.d.ts": "e6802cbf02453ac71573bcff5ae8bf053423411b12330b57ae5f684fea9bb2d7",
|
|
||||||
"dist/module/import.js": "f03e602f1bf5298b7f034d25fb4c72d82fa80e1c65b8f56c292d739dab4cc522",
|
|
||||||
"dist/module/menu.d.ts": "9758ddea9ca191cacc1784510472d2e3bf25cefd50aec6399482c69dd55c6cf9",
|
|
||||||
"dist/module/menu.js": "6c81c2372580ad11ea2507f84237eeb7654411abda008f6030ea813a24925c20",
|
|
||||||
"dist/rest/eps.d.ts": "3d9d3fe9e6a37d71a1670f12184d469726f737dbd82ed002d01cbfee89f3759d",
|
|
||||||
"dist/rest/eps.js": "22507382f7e0c09e718e3743671155eacac3211ca7b33b38e8b3cf3259667ba1",
|
|
||||||
"dist/service/base.d.ts": "352fca2284346c216a6883dde2fb7b728e822f66b8eb43285468525c0832a8a8",
|
|
||||||
"dist/service/base.js": "d8f5276b77045900e2d7b3c6e10cda74cd8d81bac872bbf944b11a0b7484b370",
|
|
||||||
"dist/service/mysql.d.ts": "5f3d0d44c41f477a55c92c4c8bc6e3e7cb897981b70bb9c904bc1d99d3827a96",
|
|
||||||
"dist/service/mysql.js": "34ea6da1e9bd1c811436073d94c5b9ddb343448726e6a8a71d2cb5222ab9f7a8",
|
|
||||||
"dist/service/postgres.d.ts": "7bf898ca2c277ad03521b719c87d56fd162e2e1f9e424042a268c245750a640e",
|
|
||||||
"dist/service/postgres.js": "007d4b1a1ed3ea4aed05ff37fb1832a1ac8f0b16a1becee56e534e77167a492b",
|
|
||||||
"dist/service/query-safety.d.ts": "6ae69ec171f34f276da7eb77cd75eb2684ba8f623da5576103fd73b001bef5fe",
|
|
||||||
"dist/service/query-safety.js": "7470e7fd1e3d616b6a31a19054ab86768c4086485f6e6a3eea0fd7165916fad8",
|
|
||||||
"dist/service/sqlite.d.ts": "7490be15aa4189a9c54a47169e7f3f0b745c0a0a4ddb3aae3ebbabb35353a0a1",
|
|
||||||
"dist/service/sqlite.js": "7afa7127967f13211b6314f8a1baa03e8ca9eb3e5fb1b0958efbe70d3595a29b",
|
|
||||||
"dist/tag/data.d.ts": "fa1a7b8bba229cc7c00b9e2cb8a69c917ab98d147adf743b544a77143bac87b5",
|
|
||||||
"dist/tag/data.js": "a498d491b221f8cc19973c0450c966c93ac1c9ac35bfe383a026ea20362f329c",
|
|
||||||
"dist/util/func.d.ts": "9fe99dc5dcda2b3b77215eeeb3fb845de408d9aea86340a4c857f55d7fd45c3f",
|
|
||||||
"dist/util/func.js": "64ce78b9b5115de344c0c11790f658f2034bde0bd262640edb1d263a5130004e",
|
|
||||||
"dist/util/location.d.ts": "bfbacbcd75579c1b6bd54cdafc6f7a152468802321a8fe0f7de3756073fd2334",
|
|
||||||
"dist/util/location.js": "2f1d906b82b6cae0a46797fc1683f822b688ab40800671268de565c0497ceb91",
|
|
||||||
"index.d.ts": "b764a7488d90d3db5bc04ed44bbe06994574d25c052a139c15e9b11ffe1be954",
|
|
||||||
"package.json": "dcd4fc7fe938b32677091f4530dfd102330f8710a940b2ccf6c1f73fa194b9bb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "@cool-midway/module-runtime",
|
|
||||||
"version": "9.0.3",
|
|
||||||
"registry": "https://npm.blendercg.art/",
|
|
||||||
"tarball": "https://npm.blendercg.art/@cool-midway/module-runtime/-/module-runtime-9.0.3.tgz",
|
|
||||||
"integrity": "sha512-hHczZpeZQBec6HeMCet4pXeAz3FzlNlUPeE7SnrexItatF9xoiKuT5KfcccXcxBSYmYGmkpnuEnntHM+//freg==",
|
|
||||||
"sha256": "4e6007e753a6c7fbbb6ddb29cfa6198689ea45b7ad866140b317ddf091088023",
|
|
||||||
"files": {
|
|
||||||
"adapter.d.ts": "ffa850716087cae3e3ef5f5bb54835143d16bcb3ad42b044c0f16aba08209074",
|
|
||||||
"dist/action.d.ts": "8d0c6d6055c1acc4a6a6e70fdb83a70a81693965b20adf2afa0290245cd649cd",
|
|
||||||
"dist/action.js": "86b35ec3d9d020f253cfa42f8030444da39a9742f6480c94010cc342056e0e4a",
|
|
||||||
"dist/adapter.d.ts": "5d69aa3eeeb1406731554f9eec8c770b7e83fe92994dc205a2b6df74ab1bd841",
|
|
||||||
"dist/adapter.js": "d43aa81f5bc89faa359e0f97c814ba25155591ff078fbb9bfd40f8c7c9683230",
|
|
||||||
"dist/business-reject.d.ts": "dfa50bf278f47097aebfb6958ff80df127f0af978031653ad20d485e119b1f93",
|
|
||||||
"dist/business-reject.js": "1bea195b38fbcce97512400be8fe43f01596a03aa5ea5d6546346064c2cd1b03",
|
|
||||||
"dist/configuration.d.ts": "5603f82c62078b962be9f9a32690a66572cc41bd9e6cda38d0fe7f53ad63e14a",
|
|
||||||
"dist/configuration.js": "fed0a7dc7e6aa6e233f8d66109589b15ccd6b0f3fdd46dc6d55bbd8c38b45311",
|
|
||||||
"dist/connection.d.ts": "b9ef495c9d54c0b0b3c47b2955dce1d3c31aaa32367fb4a4fb28aa21f128730b",
|
|
||||||
"dist/connection.js": "f265f332bc5ce7a95382dade696a738ccaf8be58865a1ffb457115cad0b5605b",
|
|
||||||
"dist/consumer.d.ts": "8c3a5ddee80f5eb59d72711ece771edb11829e5d8060ac70e879edab9179cb4d",
|
|
||||||
"dist/consumer.js": "780bb3da26cff14e6f8322dadefa8e088f18323e61113efcb2176a12e397f252",
|
|
||||||
"dist/context.d.ts": "90d189268561122a6619fb1ab406e19610bd07631e0ab4be8a7e302cfbe2dc74",
|
|
||||||
"dist/context.js": "eb2b3c97119f94c7f52498b591b7e63fe51b1ef8194b3b9cf6dbea6d0af555e7",
|
|
||||||
"dist/crud-host.d.ts": "9db03a23d96f3e982814ccc432209ffb36923bdc0e44b971121eb6c71a720364",
|
|
||||||
"dist/crud-host.js": "19ab92b9744f8cb671c4dcbfce288160c60e008f14a0b018f38a1c0de55aa4b8",
|
|
||||||
"dist/crud.d.ts": "ac9c86a22ac52a19314707eac25e4a148b25fcfc9e6b0b68c9be05685cc0524e",
|
|
||||||
"dist/crud.js": "4d6affec833831e20e21c5bba4cbdd993454e33bab62af53891d6ce6b4720576",
|
|
||||||
"dist/data.d.ts": "7a0a42b4f3bb4c437168ec7a0ed8cb85380dfb17598629abd95c373be91db352",
|
|
||||||
"dist/data.js": "575f801b991a7c35af9a9d8b33374976bb8e87948f3fee6ba10b45e957bcb15a",
|
|
||||||
"dist/decorator/event.d.ts": "23cb20746ac54b5846774e03496c4f335c265773c044795ea07d727e7fe4859b",
|
|
||||||
"dist/decorator/event.js": "3801cf45836398239734611d4c58ea0261d32e870544a770e7ad667704aad143",
|
|
||||||
"dist/error.d.ts": "890eaac4c178a55c7e4c4c606a31584e9dc29da3baeebf97e5d45d734813b924",
|
|
||||||
"dist/error.js": "f2dced066aef06c895d00285ecf69d52203bfce1635539a4b8c88d9de2a733d3",
|
|
||||||
"dist/event-writer-migration.d.ts": "dd22ca345f7b15a5297b4c0d2265d1fe81fbb4507455c6aacfd037d85e7acf93",
|
|
||||||
"dist/event-writer-migration.js": "deee4d33d7c3049f2ab9f6a9605cfea17aaf9536db7d3d1305f4903d48ff5e4f",
|
|
||||||
"dist/event-writer.d.ts": "a3808988437bc0633a718d6600dfb3a2a930f25be7daeb69d61c2f55aa2b7171",
|
|
||||||
"dist/event-writer.js": "99b400c196b3422c8e831e22352cbdf39c917511e81c2c5874ba0fc9aa970224",
|
|
||||||
"dist/executor.d.ts": "223273e6ab47dec868be23b1bfdd24b479eb47d8fb8eb903cc9e41c253925c5a",
|
|
||||||
"dist/executor.js": "9886faf012b7a975352e481ee74b7f20d8572e24ee634e059372766989dae8fd",
|
|
||||||
"dist/generated-module.d.ts": "91a115bf54da00ca545f675a6f5cd1b5230c15028f9ac44afeafc034d7e5edd9",
|
|
||||||
"dist/generated-module.js": "84739644067c446863ad34166602d0dcec9212661c61cbeea5bfe85a113b4c0a",
|
|
||||||
"dist/index.d.ts": "97ebc39d085af1c4ecccaf80c6581f9b44a725be3e2d3b0aacf809738379edd4",
|
|
||||||
"dist/index.js": "c0de3d4fae6a2f28f5ee89519c1daf7c36dc7bcc13aeccc0372999fec4b703c9",
|
|
||||||
"dist/injected-action.d.ts": "1fa4714aeceeba0ec6c5bfc1b7afcfe1c92bb5000e77db6a82122395535a2532",
|
|
||||||
"dist/injected-action.js": "3fdee7be7fee4e07c41884cbc43cb23bbd1dd4ef0ffe49fee37c53f2a8d8ca97",
|
|
||||||
"dist/injected-crud.d.ts": "b4a9f3c9756f6b13534ec2a643a905a5e9374ab3f5a6aeeb33b7a6cb9b481ef5",
|
|
||||||
"dist/injected-crud.js": "5f4593c1b12a50765f4112381a0db9f732202b908f84b6f13225da7a567eba0c",
|
|
||||||
"dist/internal.d.ts": "9d2e7f91b5cbaa2ff2dcd7af9ce462bc952f922f5a925092ea97d64957dc2f88",
|
|
||||||
"dist/internal.js": "f17a9720f74d54ffe2186cafe87157dce3ce00c3186d1f7837df7144a42adae6",
|
|
||||||
"dist/inventory.d.ts": "a947c0471139debd82c71092952027b5e645dcf6b04de6a5706630fd7ac388d2",
|
|
||||||
"dist/inventory.js": "c97c12ecb113302e4d21deef52a6d6b6b477ae2e192853d664211e19f539cd74",
|
|
||||||
"dist/json-schema.d.ts": "13da8d4cdc571ca7b38c5ae7af9a5877ead2dc2d66cbca9f4a5a20069b8bfdea",
|
|
||||||
"dist/json-schema.js": "ef1a378710cfc285679fdb8130750169bc09e19611a2e577ef081b44e5f2eeb0",
|
|
||||||
"dist/managed-action.d.ts": "41f457e76b0cefba70b909dd36d33515a81e95414f67e63af8e975e7349ba136",
|
|
||||||
"dist/managed-action.js": "fb42b5f0b1992e2b2b3f5176081a2de06cac020ca3ed744f46d93f094cfe96dd",
|
|
||||||
"dist/model.d.ts": "365b9e72920161a8b21775ab2541cd9aa31380f608bb57da01d7797e10433fc5",
|
|
||||||
"dist/model.js": "9e548ad45b0cac4642ae9437cbcd52a6deb5dd316de5242ce601c2765f68554a",
|
|
||||||
"dist/outbox.d.ts": "ff1ac6f4eed5b7f1c6001ce286d5eb92a78b118688f3fa554264cd9175bb2a3e",
|
|
||||||
"dist/outbox.js": "f9c2b986ecf8742a7d37b8a0c546973f2bc4db9749ad91ae1dba06ab6cb08fe0",
|
|
||||||
"dist/projection-consumer.d.ts": "4e7c0f6cdbce1abb3a73c57aec0af7898f1f70d1e6007c384da6efb083695e2a",
|
|
||||||
"dist/projection-consumer.js": "f58face6f749d8711cf5d3ea70545060f0f808e508a1a838ea6544e2baafca4a",
|
|
||||||
"dist/projection-fanout.d.ts": "e222a92545786e5e8708b9f858a9cb7f840b83c88e04b4d86c63d804801f1683",
|
|
||||||
"dist/projection-fanout.js": "85ae64701bc82ef810241b1703a4ca4cdff532f3184c55072de668953bc7a886",
|
|
||||||
"dist/projection-observability.d.ts": "185d60d16b64d7b0c59838ee9aaa3de558411d71d09c697f7cfc31816d83560a",
|
|
||||||
"dist/projection-observability.js": "1329606a57df9efb5916df8e700680007c3aa4533dcd28b659ca37d6167056ea",
|
|
||||||
"dist/read-model.d.ts": "8da05387b66b3bf90300ab06600e25327571fd9effe668fd67abe0053b8f65dd",
|
|
||||||
"dist/read-model.js": "038c095d2452474d01e44e2dc1f4321ea193293b6777154e2a82d07af1cf5ced",
|
|
||||||
"dist/readonly-sql.d.ts": "874ebfe1ac214f969f46fc1e1a25a2c301f610f3258570261e1e977196982de8",
|
|
||||||
"dist/readonly-sql.js": "2ad61f3b5159ef5d62a8cc3f1d35210058d78629ff4d1dd2ba7ca109781c7b32",
|
|
||||||
"dist/result-normalizer.d.ts": "a7dceb5b8acba49efb5e4b08821a43f81acea79bcc87ce0386c48e8c7e09b328",
|
|
||||||
"dist/result-normalizer.js": "2c8da8f3926a3631e92479fbb8ef25865eba56df2934ec5066917333a21aab60",
|
|
||||||
"dist/runtime-id.d.ts": "a36a22125f8abb297965707aa59f37506fb725acedfd9fe3e4f94876386acd67",
|
|
||||||
"dist/runtime-id.js": "90d0991517b910067d85a47f49fa4148a40282236d03d52a80d02fe1de512dfb",
|
|
||||||
"dist/safe-error.d.ts": "80209c9b0107ee6a2fd9db589de8a347f2e7efe31a74e043ed9d3529632947a7",
|
|
||||||
"dist/safe-error.js": "8404ea72f5d44bf3016cd6a0e3ac9a010869ac1b9c1d166ae953f0d7c0e67c6d",
|
|
||||||
"dist/saga-contract.d.ts": "1598b7adf45652f32557de74c912049f7cdf97326420c0fef3b762d256bcca88",
|
|
||||||
"dist/saga-contract.js": "22fd8729982dfa9e7824e0fc6c27a0839989c8f2ca70853d1a985b4df2bc4324",
|
|
||||||
"dist/saga.d.ts": "da12bdaef5c5d082ddae56c7e355831ae948bb77c9fb9a9ffa5d671201110231",
|
|
||||||
"dist/saga.js": "f6f14c5f5bdc692998855fa0ec698b1c5092d2eb626198af47e2b382f3c655cd",
|
|
||||||
"dist/schema.d.ts": "63f1460c8975047963048c5bae295a80f2e63f5e7e9110ff9ac6bd40f881303d",
|
|
||||||
"dist/schema.js": "85a08a3fcf152697af3806dfebc3a3127a11c9b3a6fff543645e235e147467ae",
|
|
||||||
"dist/types.d.ts": "a22b2d986097a6d1b7cce7dcd9815807a9d46e3ba98cbba06783cba9d8839928",
|
|
||||||
"dist/types.js": "d43aa81f5bc89faa359e0f97c814ba25155591ff078fbb9bfd40f8c7c9683230",
|
|
||||||
"dist/workflow.d.ts": "305566d5e30723285b2f4cc227577a8c003a7dd9f1628796fa8fe825c45874fa",
|
|
||||||
"dist/workflow.js": "79e25c6e0dc24c6b2277c18b02bedf4fda60754947ae1917b0b9cb2eecb53af3",
|
|
||||||
"dist/write-fence.d.ts": "1623b1129c45481d165b604f7ca8090bfdc4eef278aab1a3dc1740cbf42bae15",
|
|
||||||
"dist/write-fence.js": "95c6af20da75ca92a0149023ebbc515525ddbcd50da3e284209b77536bc2a5f2",
|
|
||||||
"index.d.ts": "4b4662b528dcdf65aea4f64db2ec4cc5f1ab9ca27ace965f7f3824cedcd12ec6",
|
|
||||||
"internal.d.ts": "647bd0edcc65c231dc7d03cf81bbd77b9dd063f1d9747abaee3ef10f449871e2",
|
|
||||||
"package.json": "2e125d623e4bf07bda979f4afe29a246ceeecf0c7e9da4745f4179415566fba5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "@cool-midway/rpc",
|
|
||||||
"version": "9.0.3",
|
|
||||||
"registry": "https://npm.blendercg.art/",
|
|
||||||
"tarball": "https://npm.blendercg.art/@cool-midway/rpc/-/rpc-9.0.3.tgz",
|
|
||||||
"integrity": "sha512-Sxks1b2Vzvo08SlupHM3QUnQay6vXL8eibWIzpzm7mzMJONLpibr7mKT1KFgGxeXaG3pcCWl0j1D9wpwRS233w==",
|
|
||||||
"sha256": "b00134a200053331607f09604e38dfecddb363ed9f047814a2d00cd9b464fcfa",
|
|
||||||
"files": {
|
|
||||||
"dist/action-registry.d.ts": "40231dd8299c0bfae1b006105d49945eb2f20a40334856db32e4b7cc2851398b",
|
|
||||||
"dist/action-registry.js": "04f77c61445d47f66eaad984ee8e059a38bb0e610d2f584f5754d5a0ead105c5",
|
|
||||||
"dist/caller-context.d.ts": "3f58cb5d653e2d5179996c3302dddc5c4bfdc3ff2ac501ca8d6dcedb4cb606d5",
|
|
||||||
"dist/caller-context.js": "688a823ed382ded556ef7c8d6b151d7e9f65fef56781f7d2ba9443b83877b680",
|
|
||||||
"dist/circuit-breaker.d.ts": "04a630ec3eff5b0c38aebc5aa0f217234687991cdd15bb34a1ff901040aac3af",
|
|
||||||
"dist/circuit-breaker.js": "0fe32c6019007dc9499c7566abecb63741ade5a70a336501d922986da50a5fc7",
|
|
||||||
"dist/codec.d.ts": "69a5f0f77945ca937a6e0dc7c99e7a5b0e13c6c672ff528e36d125293aa390ed",
|
|
||||||
"dist/codec.js": "1ba6e712f9c7c643267928ffdf6a3997d7d4ed52d51b8082f34938b75b48d127",
|
|
||||||
"dist/config/config.default.d.ts": "42220f9e583d1e7294df9d24a6849b55f95ad04294209833a6d86e0a7fe7c211",
|
|
||||||
"dist/config/config.default.js": "8afe036a5ac3e4c1a382dddc1bec0a5599129977c2c28fc4d9f3777791f5444b",
|
|
||||||
"dist/configuration.d.ts": "0a0c26f470cacf671444db225efb4433d8fb097d6e97f6f22bb4bbc9aebbb89d",
|
|
||||||
"dist/configuration.js": "8e0ad7f8b784a39e6859fe18fb5fda56d44fa82d45ab15c9095ab2072d8187a3",
|
|
||||||
"dist/decorator/rpc.d.ts": "eee9097a4845b107033ca9234ef8e6f089e03f130243250641bf0f8746b26725",
|
|
||||||
"dist/decorator/rpc.js": "14552fea525d683fc459fab2d9b846af70ec77aa4032e275dcf3b0d09288cedc",
|
|
||||||
"dist/error.d.ts": "f89c87f491f25efd6fe247423b50dfc14908bb478cc5daf32a0e37e0cab3135b",
|
|
||||||
"dist/error.js": "8ab6a31e98e83ffbb105b9e33ea8801d9120c3192f5d94f6e782bcc4fc1c87a9",
|
|
||||||
"dist/event-transport.d.ts": "27ebe825bcfe6cd78fd1ca3f05cbb9a3315b5e075f0f3250c2e95ed2ed7fd1dd",
|
|
||||||
"dist/event-transport.js": "c091d60b55e6149d9b727220f41445545df57e102a2d27c5cb9a8bc47c71df55",
|
|
||||||
"dist/identity.d.ts": "5e7a2f9cbe80816cad22f678663f814f89e2295ecfb0b6d7436a8a1efb166f7c",
|
|
||||||
"dist/identity.js": "fbaa0c3b1c7a09ebb6b7ade4989c96b97db80bf0048a9059f054f048f78bc905",
|
|
||||||
"dist/index.d.ts": "d4ac7f2d87a93a50263ccbb3da781dcf4d3db9775ae5ba316327862f0c007d69",
|
|
||||||
"dist/index.js": "26d61f60277e769fa7f9379fc2a98af9840296ea7c4d0e65c8c354bf70d37bdd",
|
|
||||||
"dist/injected-action.d.ts": "10954865dd4ddd1f1bfd867e151d4fda993547fc206346d5ccf39a241d40527e",
|
|
||||||
"dist/injected-action.js": "92fdaaa784e64b0babbcba16a941d3569367a47ccdc2137d7afc415e542f7bc9",
|
|
||||||
"dist/managed-runtime.d.ts": "f9c4f933c09ee7488d05cad68cd411de8239998a48738f2a90988aff6f691379",
|
|
||||||
"dist/managed-runtime.js": "1985cb709666c9f6bba561b1756cbc2420fd068e138f82180712a3c577760373",
|
|
||||||
"dist/mode.d.ts": "cf6a400f1498ce1102c70f47719d6c58dfe3555a8aacce03eb09c7ea38c8d2b7",
|
|
||||||
"dist/mode.js": "b986fe6d3623d6baad7ecd44496c1fa681203c934013af8baaa330ba371fc75d",
|
|
||||||
"dist/redis.d.ts": "794e9c53f6c36494562d1685483d826794af29218244d2692e2ce9e1a20163eb",
|
|
||||||
"dist/redis.js": "871b8b7f906a83ce032b8a23d93471fed49c0e2a5baa5be3fab392a8e80eef9c",
|
|
||||||
"dist/rpc.d.ts": "4f69d0c71471a53acfff1a92fbae44dd141213dd4151546d92765273ef843d31",
|
|
||||||
"dist/rpc.js": "91f67887ba84348fd15e534a7abb221c17c252c67f0f7666f746aa3bd6a94a7e",
|
|
||||||
"dist/runtime-log.d.ts": "2ae04a0fad229a7b2c207667f77492881898bdd5ed4199f0402bf5df2a4072d4",
|
|
||||||
"dist/runtime-log.js": "672e35f1bb2c648ae58d2c55f724997a1e2a45bd2e62a8cd8180283f31d8b093",
|
|
||||||
"dist/saas-host/bootstrap.d.ts": "f639edef99770f091ac90e737a0ab2c857830241a8996171492768d108ad43f0",
|
|
||||||
"dist/saas-host/bootstrap.js": "a53eb6f40c7a7c2427fbcd0cde78e8985f64dda15fcff1843e8957f5e7086271",
|
|
||||||
"dist/saas-host/config.d.ts": "f4ed4b0c1c36f74ef24d16047139999912394a6a9a2164ca2cbcec6c762a82d2",
|
|
||||||
"dist/saas-host/config.js": "252dd8da719b22cb55011f734d9fc3f28dde1ebe36b06eaa4123b869b4015a70",
|
|
||||||
"dist/saas-host/configuration.d.ts": "2b18a8a64cceb9a9c5b0e5d5485e1f125a504bda01c9884a5920d17ab3d6c88c",
|
|
||||||
"dist/saas-host/configuration.js": "555560131fe84336cc967de372afc2af842cfce7de43f0e8b1e0384a90259872",
|
|
||||||
"dist/saas-host/control-plane.d.ts": "9c096d15d2e6a4ed33b41393e45e8895f1baaaa16630304c0cb3fc272db5baec",
|
|
||||||
"dist/saas-host/control-plane.js": "5ef87b9e5fbeabed216a3100b83070fb510e545b1bdda687ed99db00c175125d",
|
|
||||||
"dist/saas-host/credential-store.d.ts": "f4a9120e4aefcabb78903697fa30a5e7fcef9c8eeb347a0d8fae60c7f94c1f9d",
|
|
||||||
"dist/saas-host/credential-store.js": "126587a35efbe86516eed33c26f98f394ad388061397511eefb30879acedbb6f",
|
|
||||||
"dist/saas-host/index.d.ts": "2e5e01089a7a42c312456f44054a33a426ef18572affd687e79a90acb58956c9",
|
|
||||||
"dist/saas-host/index.js": "ca038796a56260424afbea02215180fa319208403ddd2e5027901eb927277823",
|
|
||||||
"dist/saas-host/providers.d.ts": "f0b97f3a19b09e42d85acf1053cf0799547cb92a976369b9a16d79ffc9c03dd3",
|
|
||||||
"dist/saas-host/providers.js": "3729b5c902e4ba142e1bd8fef6e973c48509c8f57176ef39c7593c7e1bd67249",
|
|
||||||
"dist/saas-host/runtime.d.ts": "20f2ce0293b4fa315af93e334901f2f6acffa9867f8e60ae62a78a984dda129b",
|
|
||||||
"dist/saas-host/runtime.js": "7a3e79d3b8b195f5de38eb22e40dcd8d284a58fac26cf8f6231c3c636bcc5d65",
|
|
||||||
"dist/saas-host/security.d.ts": "e03aa4553fe661b2c89f5c062c68050d6350f14bc7879485540b497dc880adf7",
|
|
||||||
"dist/saas-host/security.js": "f25567da27233ebc3ad88ba3a0769163afeaf38b8f1eb92733446e3b5b9ab626",
|
|
||||||
"dist/signer.d.ts": "b0859783023e91f0326a702c91780cb436f5e726a8cc04860b3a408b81130cae",
|
|
||||||
"dist/signer.js": "a941011019527562c3ea2c1373523dbdeabb793d1a1dfdbb6a68d97022510916",
|
|
||||||
"dist/test.d.ts": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
||||||
"dist/test.js": "5386504311845b9dc3fe230d3c5a3b5ab3c6d36154add15e91d32d8442c25157",
|
|
||||||
"dist/types.d.ts": "c6567480b4881e9748d31e1f4935ae99318c44db4eee16558859732bcf06a5f8",
|
|
||||||
"dist/types.js": "d43aa81f5bc89faa359e0f97c814ba25155591ff078fbb9bfd40f8c7c9683230",
|
|
||||||
"index.d.ts": "b764a7488d90d3db5bc04ed44bbe06994574d25c052a139c15e9b11ffe1be954",
|
|
||||||
"package.json": "d3d7a018d4f73ddba3d040dd1e8fef63ae9dd75517743b09f2b590248c27f063",
|
|
||||||
"saas-host.d.ts": "cdc6609dcbb75e109b15e8644a4db268f303d23005a6338078ec215eef15a530",
|
|
||||||
"saas-host.js": "7c191f478052db1c7e876cf33aad154f879a33fa194f1cd1d216d949598dd09b"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
// Published baselines take precedence. Invalid published data never falls
|
|
||||||
// back to vendor candidates. This verifier performs no network/lifecycle work.
|
|
||||||
const fs = require('node:fs');
|
|
||||||
const path = require('node:path');
|
|
||||||
const { createHash } = require('node:crypto');
|
|
||||||
const hash = data => createHash('sha256').update(data).digest('hex');
|
|
||||||
const packageName = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
||||||
const exactVersion = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
||||||
const sha256 = /^[a-f0-9]{64}$/;
|
|
||||||
const checkedFile = relative => /(?:\.(?:c|m)?js|\.d\.ts)$/.test(relative) || path.posix.basename(relative) === 'package.json';
|
|
||||||
function fail(message) { throw new Error(message); }
|
|
||||||
function recordPath(relative) {
|
|
||||||
if (typeof relative !== 'string' || !relative || relative.includes('\\') || relative.startsWith('/') || relative.includes(':') || /[\x00-\x1f]/.test(relative) ||
|
|
||||||
relative.split('/').some(part => !part || part === '.' || part === '..') || !checkedFile(relative)) fail('Unsafe or unsupported baseline file path');
|
|
||||||
return relative;
|
|
||||||
}
|
|
||||||
function containedFile(root, relative) {
|
|
||||||
const actual = fs.realpathSync(path.join(root, recordPath(relative)));
|
|
||||||
if (!actual.startsWith(root + path.sep) || !fs.statSync(actual).isFile()) fail('Installed file escapes package root');
|
|
||||||
return actual;
|
|
||||||
}
|
|
||||||
function installedFiles(directory, prefix = '') {
|
|
||||||
return fs.readdirSync(directory, { withFileTypes: true }).flatMap(entry => {
|
|
||||||
if (entry.name === 'node_modules') return [];
|
|
||||||
const relative = prefix + entry.name;
|
|
||||||
// pnpm may link the package root; links within the artifact are forbidden.
|
|
||||||
if (entry.isSymbolicLink()) fail('Unexpected link inside installed package');
|
|
||||||
return entry.isDirectory() ? installedFiles(path.join(directory, entry.name), relative + '/') : checkedFile(relative) ? [relative] : [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function metadataUrl(value) {
|
|
||||||
if (typeof value !== 'string') fail('Published registry metadata missing');
|
|
||||||
let url;
|
|
||||||
try { url = new URL(value); } catch { fail('Published registry metadata invalid'); }
|
|
||||||
if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password || url.search || url.hash) fail('Published registry metadata must be a credential-free HTTP URL');
|
|
||||||
}
|
|
||||||
function validateRecord(record, published) {
|
|
||||||
if (!record || typeof record !== 'object' || !packageName.test(record.name || '') || !exactVersion.test(record.version || '') ||
|
|
||||||
!record.files || typeof record.files !== 'object' || Array.isArray(record.files) || !Object.keys(record.files).length) fail('Invalid package baseline');
|
|
||||||
for (const [relative, checksum] of Object.entries(record.files)) {
|
|
||||||
recordPath(relative);
|
|
||||||
if (!sha256.test(checksum)) fail('Invalid baseline file digest');
|
|
||||||
}
|
|
||||||
if (published) {
|
|
||||||
metadataUrl(record.registry); metadataUrl(record.tarball);
|
|
||||||
const integrity=typeof record.integrity === 'string' && /^sha(256|384|512)-([A-Za-z0-9+/]+={0,2})$/.exec(record.integrity);
|
|
||||||
if (!integrity || Buffer.from(integrity[2], 'base64').length !== Number(integrity[1])/8 || Buffer.from(integrity[2], 'base64').toString('base64') !== integrity[2]) fail('Published integrity missing or invalid');
|
|
||||||
if (!Object.hasOwn(record.files, 'package.json')) fail('Published baseline must include package.json');
|
|
||||||
} else if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*\.tgz$/.test(record.archive || '') || !sha256.test(record.sha256 || '')) fail('Invalid vendor archive baseline');
|
|
||||||
}
|
|
||||||
function verifyPackages(projectRoot = path.resolve(__dirname, '..')) {
|
|
||||||
const root = fs.realpathSync(projectRoot);
|
|
||||||
const publishedPath = path.join(root, 'scripts/published-packages.json');
|
|
||||||
let published = false;
|
|
||||||
try { fs.lstatSync(publishedPath); published = true; } catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
||||||
const baseline = JSON.parse(fs.readFileSync(published ? publishedPath : path.join(root, 'vendor/local-packages.json'), 'utf8'));
|
|
||||||
if (baseline.formatVersion !== 1 || !Array.isArray(baseline.packages) || !baseline.packages.length) fail('Unsupported or empty package baseline');
|
|
||||||
const manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
||||||
const names = new Set(); let count = 0;
|
|
||||||
for (const record of baseline.packages) {
|
|
||||||
validateRecord(record, published);
|
|
||||||
if (names.has(record.name)) fail('Duplicate package baseline');
|
|
||||||
names.add(record.name);
|
|
||||||
const expectedSpecifier = published ? record.version : `file:vendor/${record.archive}`;
|
|
||||||
const declarations = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']
|
|
||||||
.filter(section => Object.hasOwn(manifest[section] || {}, record.name)).map(section => manifest[section][record.name]);
|
|
||||||
if (!declarations.length || declarations.some(value => value !== expectedSpecifier)) fail(`Unpinned dependency ${record.name}`);
|
|
||||||
if (!published && hash(fs.readFileSync(path.join(root, 'vendor', record.archive))) !== record.sha256) fail(`Archive drift ${record.name}`);
|
|
||||||
// Resolve pnpm virtual stores and package.json exports through Node.
|
|
||||||
const metadataPath = require.resolve(`${record.name}/package.json`, { paths: [root] });
|
|
||||||
const installed = fs.realpathSync(path.dirname(metadataPath));
|
|
||||||
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
|
|
||||||
if (metadata.name !== record.name || metadata.version !== record.version) fail(`Installed identity drift ${record.name}`);
|
|
||||||
const extra = installedFiles(installed).filter(relative => !Object.hasOwn(record.files, relative));
|
|
||||||
if (extra.length) fail(`Unexpected executable or metadata files ${record.name}: ${extra.join(', ')}`);
|
|
||||||
for (const [relative, checksum] of Object.entries(record.files)) {
|
|
||||||
// Historical vendor packages may normalize metadata. Published mode
|
|
||||||
// instead checks package.json against the final published artifact.
|
|
||||||
if (!published && relative === 'package.json') continue;
|
|
||||||
if (hash(fs.readFileSync(containedFile(installed, relative))) !== checksum) fail(`Installed byte drift ${record.name}/${relative}`);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { mode: published ? 'published' : 'vendor', packages: baseline.packages.length, files: count };
|
|
||||||
}
|
|
||||||
module.exports = { verifyPackages };
|
|
||||||
if (require.main === module) {
|
|
||||||
const result = verifyPackages();
|
|
||||||
console.log(`Verified ${result.packages} ${result.mode} packages and ${result.files} installed files.`);
|
|
||||||
}
|
|
||||||
@@ -20,9 +20,9 @@ export class Orders extends SaasTableModel<OrdersRow> {
|
|||||||
static readonly definition = defineSaasTableModel({
|
static readonly definition = defineSaasTableModel({
|
||||||
"kind": "table",
|
"kind": "table",
|
||||||
"moduleCode": "order",
|
"moduleCode": "order",
|
||||||
"revision": 1,
|
"revision": 2,
|
||||||
"schemaVersion": 1,
|
"schemaVersion": 2,
|
||||||
"schemaInputChecksum": "1b3eec01e5b04880166e20e8e6df70b79f227129ea9165e38cfbb925be3c2d23",
|
"schemaInputChecksum": "34ed49d330f45bd9933b582bc9265cf0f4e2e2683833d0f39e38a357d138ab65",
|
||||||
"tableCode": "orders",
|
"tableCode": "orders",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { defineGeneratedSaasModule } from '@cool-midway/module-runtime';
|
|||||||
export const GeneratedSaasModule = defineGeneratedSaasModule({
|
export const GeneratedSaasModule = defineGeneratedSaasModule({
|
||||||
"formatVersion": 2,
|
"formatVersion": 2,
|
||||||
"moduleCode": "order",
|
"moduleCode": "order",
|
||||||
"revision": 1,
|
"revision": 2,
|
||||||
"schemaVersion": 1,
|
"schemaVersion": 2,
|
||||||
"schemaInputChecksum": "1b3eec01e5b04880166e20e8e6df70b79f227129ea9165e38cfbb925be3c2d23",
|
"schemaInputChecksum": "34ed49d330f45bd9933b582bc9265cf0f4e2e2683833d0f39e38a357d138ab65",
|
||||||
"runtimeContractChecksum": "3d025d1b634c413449eb91164fb1f859a388d8930d2de112980a606b591befce",
|
"runtimeContractChecksum": "ba026a37cab981d8bdc1ed59db977b7c10df5f0528eaed02202e1303dc3d1194",
|
||||||
"implementationManifestChecksum": "f25647d2dd90e14d6226683d2ff67ba32132b2dfdeb861015805240a96d4635b"
|
"implementationManifestChecksum": "f25647d2dd90e14d6226683d2ff67ba32132b2dfdeb861015805240a96d4635b"
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -83,10 +83,10 @@ export const SaasActions = {
|
|||||||
"actionChecksum": "3978a7ba9a3e0f91842031fd323ec63b3816eef68cb8dc82ffad6d187de8b7ea",
|
"actionChecksum": "3978a7ba9a3e0f91842031fd323ec63b3816eef68cb8dc82ffad6d187de8b7ea",
|
||||||
"outputChecksum": "fe644424ef6a9e462b56edc9f70f48bb218783c69469469d4310de48589a0df5",
|
"outputChecksum": "fe644424ef6a9e462b56edc9f70f48bb218783c69469469d4310de48589a0df5",
|
||||||
"contractVersion": 1,
|
"contractVersion": 1,
|
||||||
"providerReleaseId": 38,
|
"providerReleaseId": 41,
|
||||||
"consumerModuleCode": "order",
|
"consumerModuleCode": "order",
|
||||||
"providerModuleCode": "demo",
|
"providerModuleCode": "demo",
|
||||||
"providerReleaseVersion": "1.0.0-rc.1"
|
"providerReleaseVersion": "1.0.1"
|
||||||
}),
|
}),
|
||||||
GoodsReleaseStock: defineInjectedSaasAction<DemoGoodsReleaseStockInput, DemoGoodsReleaseStockOutput>({
|
GoodsReleaseStock: defineInjectedSaasAction<DemoGoodsReleaseStockInput, DemoGoodsReleaseStockOutput>({
|
||||||
"kind": "action",
|
"kind": "action",
|
||||||
@@ -148,10 +148,10 @@ export const SaasActions = {
|
|||||||
"outputChecksum": "a632b9f89518c3c17861454b74173a4880d5e8482639d3ff7f761aee4d061bd9",
|
"outputChecksum": "a632b9f89518c3c17861454b74173a4880d5e8482639d3ff7f761aee4d061bd9",
|
||||||
"contractVersion": 1,
|
"contractVersion": 1,
|
||||||
"commandEventType": "saas.workflow.demo.goods.releaseStock",
|
"commandEventType": "saas.workflow.demo.goods.releaseStock",
|
||||||
"providerReleaseId": 38,
|
"providerReleaseId": 41,
|
||||||
"consumerModuleCode": "order",
|
"consumerModuleCode": "order",
|
||||||
"providerModuleCode": "demo",
|
"providerModuleCode": "demo",
|
||||||
"providerReleaseVersion": "1.0.0-rc.1",
|
"providerReleaseVersion": "1.0.1",
|
||||||
"commandEventContractChecksum": "18a727b91cf9410c3b7ac14e3a8799dbbfa8937f84667bfe7e31e21e662396be"
|
"commandEventContractChecksum": "18a727b91cf9410c3b7ac14e3a8799dbbfa8937f84667bfe7e31e21e662396be"
|
||||||
}),
|
}),
|
||||||
GoodsReserveStock: defineInjectedSaasAction<DemoGoodsReserveStockInput, DemoGoodsReserveStockOutput>({
|
GoodsReserveStock: defineInjectedSaasAction<DemoGoodsReserveStockInput, DemoGoodsReserveStockOutput>({
|
||||||
@@ -208,10 +208,10 @@ export const SaasActions = {
|
|||||||
"outputChecksum": "545bd8b0bf6eb8670b21497236038187ee0f1df1544a318b16a399458fcb796e",
|
"outputChecksum": "545bd8b0bf6eb8670b21497236038187ee0f1df1544a318b16a399458fcb796e",
|
||||||
"contractVersion": 1,
|
"contractVersion": 1,
|
||||||
"commandEventType": "saas.workflow.demo.goods.reserveStock",
|
"commandEventType": "saas.workflow.demo.goods.reserveStock",
|
||||||
"providerReleaseId": 38,
|
"providerReleaseId": 41,
|
||||||
"consumerModuleCode": "order",
|
"consumerModuleCode": "order",
|
||||||
"providerModuleCode": "demo",
|
"providerModuleCode": "demo",
|
||||||
"providerReleaseVersion": "1.0.0-rc.1",
|
"providerReleaseVersion": "1.0.1",
|
||||||
"commandEventContractChecksum": "ffbecad3a3d63b8bf92fde701dfaa11c8502a2c5f9f6bbb89b755e214024c3ae"
|
"commandEventContractChecksum": "ffbecad3a3d63b8bf92fde701dfaa11c8502a2c5f9f6bbb89b755e214024c3ae"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"manifestVersion": 1,
|
"manifestVersion": 1,
|
||||||
"moduleCode": "order",
|
"moduleCode": "order",
|
||||||
"revision": 1,
|
"revision": 2,
|
||||||
"schemaChecksum": null,
|
"schemaChecksum": null,
|
||||||
"schemaInputChecksum": "1b3eec01e5b04880166e20e8e6df70b79f227129ea9165e38cfbb925be3c2d23",
|
"schemaInputChecksum": "34ed49d330f45bd9933b582bc9265cf0f4e2e2683833d0f39e38a357d138ab65",
|
||||||
"actionManifestChecksum": null,
|
"actionManifestChecksum": null,
|
||||||
"runtimeContractChecksum": "3d025d1b634c413449eb91164fb1f859a388d8930d2de112980a606b591befce",
|
"runtimeContractChecksum": "ba026a37cab981d8bdc1ed59db977b7c10df5f0528eaed02202e1303dc3d1194",
|
||||||
"implementationManifestChecksum": "f25647d2dd90e14d6226683d2ff67ba32132b2dfdeb861015805240a96d4635b",
|
"implementationManifestChecksum": "f25647d2dd90e14d6226683d2ff67ba32132b2dfdeb861015805240a96d4635b",
|
||||||
"actions": [
|
"actions": [
|
||||||
"OrdersAdd",
|
"OrdersAdd",
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"formatVersion": 2,
|
"formatVersion": 2,
|
||||||
"moduleCode": "order",
|
"moduleCode": "order",
|
||||||
"revision": 1,
|
"revision": 2,
|
||||||
"releaseVersion": null,
|
"releaseVersion": null,
|
||||||
"schemaVersion": 1,
|
"schemaVersion": 2,
|
||||||
"schemaInputChecksum": "1b3eec01e5b04880166e20e8e6df70b79f227129ea9165e38cfbb925be3c2d23",
|
"schemaInputChecksum": "34ed49d330f45bd9933b582bc9265cf0f4e2e2683833d0f39e38a357d138ab65",
|
||||||
"schemaChecksum": null,
|
"schemaChecksum": null,
|
||||||
"actionManifestChecksum": null,
|
"actionManifestChecksum": null,
|
||||||
"runtimeContractChecksum": "3d025d1b634c413449eb91164fb1f859a388d8930d2de112980a606b591befce",
|
"runtimeContractChecksum": "ba026a37cab981d8bdc1ed59db977b7c10df5f0528eaed02202e1303dc3d1194",
|
||||||
"implementationManifestChecksum": "f25647d2dd90e14d6226683d2ff67ba32132b2dfdeb861015805240a96d4635b",
|
"implementationManifestChecksum": "f25647d2dd90e14d6226683d2ff67ba32132b2dfdeb861015805240a96d4635b",
|
||||||
"models": [
|
"models": [
|
||||||
{
|
{
|
||||||
@@ -2031,10 +2031,10 @@
|
|||||||
"actionChecksum": "3978a7ba9a3e0f91842031fd323ec63b3816eef68cb8dc82ffad6d187de8b7ea",
|
"actionChecksum": "3978a7ba9a3e0f91842031fd323ec63b3816eef68cb8dc82ffad6d187de8b7ea",
|
||||||
"outputChecksum": "fe644424ef6a9e462b56edc9f70f48bb218783c69469469d4310de48589a0df5",
|
"outputChecksum": "fe644424ef6a9e462b56edc9f70f48bb218783c69469469d4310de48589a0df5",
|
||||||
"contractVersion": 1,
|
"contractVersion": 1,
|
||||||
"providerReleaseId": 38,
|
"providerReleaseId": 41,
|
||||||
"consumerModuleCode": "order",
|
"consumerModuleCode": "order",
|
||||||
"providerModuleCode": "demo",
|
"providerModuleCode": "demo",
|
||||||
"providerReleaseVersion": "1.0.0-rc.1"
|
"providerReleaseVersion": "1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2101,10 +2101,10 @@
|
|||||||
"outputChecksum": "a632b9f89518c3c17861454b74173a4880d5e8482639d3ff7f761aee4d061bd9",
|
"outputChecksum": "a632b9f89518c3c17861454b74173a4880d5e8482639d3ff7f761aee4d061bd9",
|
||||||
"contractVersion": 1,
|
"contractVersion": 1,
|
||||||
"commandEventType": "saas.workflow.demo.goods.releaseStock",
|
"commandEventType": "saas.workflow.demo.goods.releaseStock",
|
||||||
"providerReleaseId": 38,
|
"providerReleaseId": 41,
|
||||||
"consumerModuleCode": "order",
|
"consumerModuleCode": "order",
|
||||||
"providerModuleCode": "demo",
|
"providerModuleCode": "demo",
|
||||||
"providerReleaseVersion": "1.0.0-rc.1",
|
"providerReleaseVersion": "1.0.1",
|
||||||
"commandEventContractChecksum": "18a727b91cf9410c3b7ac14e3a8799dbbfa8937f84667bfe7e31e21e662396be"
|
"commandEventContractChecksum": "18a727b91cf9410c3b7ac14e3a8799dbbfa8937f84667bfe7e31e21e662396be"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2166,10 +2166,10 @@
|
|||||||
"outputChecksum": "545bd8b0bf6eb8670b21497236038187ee0f1df1544a318b16a399458fcb796e",
|
"outputChecksum": "545bd8b0bf6eb8670b21497236038187ee0f1df1544a318b16a399458fcb796e",
|
||||||
"contractVersion": 1,
|
"contractVersion": 1,
|
||||||
"commandEventType": "saas.workflow.demo.goods.reserveStock",
|
"commandEventType": "saas.workflow.demo.goods.reserveStock",
|
||||||
"providerReleaseId": 38,
|
"providerReleaseId": 41,
|
||||||
"consumerModuleCode": "order",
|
"consumerModuleCode": "order",
|
||||||
"providerModuleCode": "demo",
|
"providerModuleCode": "demo",
|
||||||
"providerReleaseVersion": "1.0.0-rc.1",
|
"providerReleaseVersion": "1.0.1",
|
||||||
"commandEventContractChecksum": "ffbecad3a3d63b8bf92fde701dfaa11c8502a2c5f9f6bbb89b755e214024c3ae"
|
"commandEventContractChecksum": "ffbecad3a3d63b8bf92fde701dfaa11c8502a2c5f9f6bbb89b755e214024c3ae"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2209,8 +2209,8 @@
|
|||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"providerReleaseId": 38,
|
"providerReleaseId": 41,
|
||||||
"providerReleaseVersion": "1.0.0-rc.1"
|
"providerReleaseVersion": "1.0.1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"crud": [
|
"crud": [
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
import { Inject, Provide } from '@midwayjs/core';
|
import { Provide } from '@midwayjs/core';
|
||||||
|
import { createHash } from 'node:crypto';
|
||||||
import {
|
import {
|
||||||
InjectSaasAction,
|
InjectSaasAction,
|
||||||
InjectSaasCrud,
|
InjectSaasCrud,
|
||||||
ModuleRuntimeError,
|
ModuleRuntimeError,
|
||||||
SaasAction,
|
SaasAction,
|
||||||
SaasBusinessReject,
|
|
||||||
type SaasActionCaller,
|
type SaasActionCaller,
|
||||||
type SaasCrudCaller,
|
type SaasCrudCaller,
|
||||||
SaasCrudService,
|
SaasCrudService,
|
||||||
SaasCrudServiceBase,
|
SaasCrudServiceBase,
|
||||||
SaasWorkflow,
|
|
||||||
SaasActionContext,
|
|
||||||
} from '@cool-midway/module-runtime';
|
} from '@cool-midway/module-runtime';
|
||||||
import {
|
import {
|
||||||
Actions,
|
Actions,
|
||||||
@@ -20,7 +18,6 @@ import {
|
|||||||
type OrdersCancelOrderOutput,
|
type OrdersCancelOrderOutput,
|
||||||
type OrdersCreateOrderInput,
|
type OrdersCreateOrderInput,
|
||||||
type OrdersCreateOrderOutput,
|
type OrdersCreateOrderOutput,
|
||||||
type OrdersCrudBeforeChange,
|
|
||||||
type OrdersDetailWithGoodsInput,
|
type OrdersDetailWithGoodsInput,
|
||||||
type OrdersDetailWithGoodsOutput,
|
type OrdersDetailWithGoodsOutput,
|
||||||
} from '../saas/generated/order';
|
} from '../saas/generated/order';
|
||||||
@@ -30,8 +27,6 @@ type GoodsRecord = Record<string, unknown>;
|
|||||||
@Provide()
|
@Provide()
|
||||||
@SaasCrudService(Crud.Orders)
|
@SaasCrudService(Crud.Orders)
|
||||||
export class OrdersService extends SaasCrudServiceBase<typeof Crud.Orders> {
|
export class OrdersService extends SaasCrudServiceBase<typeof Crud.Orders> {
|
||||||
@Inject() private readonly workflow!: SaasWorkflow;
|
|
||||||
@Inject() private readonly actionContext!: SaasActionContext;
|
|
||||||
@InjectSaasCrud(Crud.Orders)
|
@InjectSaasCrud(Crud.Orders)
|
||||||
private readonly orders!: SaasCrudCaller<typeof Crud.Orders>;
|
private readonly orders!: SaasCrudCaller<typeof Crud.Orders>;
|
||||||
|
|
||||||
@@ -40,27 +35,15 @@ export class OrdersService extends SaasCrudServiceBase<typeof Crud.Orders> {
|
|||||||
typeof SaasActions.Demo.GoodsBatchInfo
|
typeof SaasActions.Demo.GoodsBatchInfo
|
||||||
>;
|
>;
|
||||||
|
|
||||||
protected async modifyBefore(change: OrdersCrudBeforeChange): Promise<void> {
|
@InjectSaasAction(SaasActions.Demo.GoodsReserveStock)
|
||||||
const caller = this.actionContext.managedCrudCaller;
|
private readonly reserveStock!: SaasActionCaller<
|
||||||
if (change.operation === 'add') {
|
typeof SaasActions.Demo.GoodsReserveStock
|
||||||
if (caller?.kind !== 'business' || caller.actionName !== 'orders.createOrder' || change.input.status !== 'pending_reservation') throw new SaasBusinessReject('ORDER_COMMAND_REQUIRED','创建订单必须使用下单业务命令');
|
>;
|
||||||
return;
|
|
||||||
}
|
@InjectSaasAction(SaasActions.Demo.GoodsReleaseStock)
|
||||||
if (change.operation === 'delete') throw new SaasBusinessReject('ORDER_COMMAND_REQUIRED','订单保留作为库存预留及工作流凭据,不能通过通用CRUD删除');
|
private readonly releaseStock!: SaasActionCaller<
|
||||||
const protectedFields = ['order_no','goods_id','quantity','unit_price','amount','status'];
|
typeof SaasActions.Demo.GoodsReleaseStock
|
||||||
if (!protectedFields.some(field => Object.prototype.hasOwnProperty.call(change.input,field))) return;
|
>;
|
||||||
const keys = Object.keys(change.input);
|
|
||||||
if (keys.some(key => key !== 'id' && key !== 'status')) throw new SaasBusinessReject('ORDER_COMMAND_REQUIRED','订单商品、数量和金额在创建后不可修改');
|
|
||||||
const status = change.input.status;
|
|
||||||
if (caller?.kind === 'business' && caller.actionName === 'orders.cancelOrder' && status === 'cancel_pending') return;
|
|
||||||
const outcomes: Record<string,string[]> = {
|
|
||||||
'saas.saga.step.completed':['confirmed','cancelled'],
|
|
||||||
'saas.saga.step.failed':['rejected','manual_review'],
|
|
||||||
'saas.saga.compensation.completed':['failed','manual_review'],
|
|
||||||
};
|
|
||||||
if (caller?.kind === 'saga' && outcomes[caller.eventType || '']?.includes(String(status))) return;
|
|
||||||
throw new SaasBusinessReject('ORDER_COMMAND_REQUIRED','订单受管字段只能由业务命令和工作流回执变更');
|
|
||||||
}
|
|
||||||
|
|
||||||
@SaasAction(Actions.OrdersCreateOrder)
|
@SaasAction(Actions.OrdersCreateOrder)
|
||||||
async createOrder(
|
async createOrder(
|
||||||
@@ -68,32 +51,30 @@ export class OrdersService extends SaasCrudServiceBase<typeof Crud.Orders> {
|
|||||||
): Promise<OrdersCreateOrderOutput> {
|
): Promise<OrdersCreateOrderOutput> {
|
||||||
const goods = await this.requireGoods(input.goodsId);
|
const goods = await this.requireGoods(input.goodsId);
|
||||||
if (integer(goods.status, '商品状态') !== 1) {
|
if (integer(goods.status, '商品状态') !== 1) {
|
||||||
throw new SaasBusinessReject('GOODS_UNAVAILABLE', '商品未上架,不能下单');
|
throw conflict('商品未上架,不能下单');
|
||||||
}
|
}
|
||||||
|
|
||||||
const unitPrice = decimal(goods.price, '商品价格');
|
const unitPrice = decimal(goods.price, '商品价格');
|
||||||
const amount = multiplyDecimal(unitPrice, input.quantity);
|
const amount = multiplyDecimal(unitPrice, input.quantity);
|
||||||
|
|
||||||
|
// Complete the remote write before the local eventized CRUD acquires the
|
||||||
|
// tenant-wide outbox sequence lock. Holding that lock across a synchronous
|
||||||
|
// module call would make the provider wait for this transaction itself.
|
||||||
|
await this.reserveStock(
|
||||||
|
{
|
||||||
|
goodsId: input.goodsId,
|
||||||
|
quantity: input.quantity,
|
||||||
|
},
|
||||||
|
{ idempotencyKey: businessKey('reserve', input.orderNo) }
|
||||||
|
);
|
||||||
|
|
||||||
const order = await this.orders.add({
|
const order = await this.orders.add({
|
||||||
order_no: input.orderNo,
|
order_no: input.orderNo,
|
||||||
goods_id: String(input.goodsId),
|
goods_id: String(input.goodsId),
|
||||||
quantity: input.quantity,
|
quantity: input.quantity,
|
||||||
unit_price: unitPrice,
|
unit_price: unitPrice,
|
||||||
amount,
|
amount,
|
||||||
status: 'pending_reservation',
|
status: 'created',
|
||||||
});
|
|
||||||
|
|
||||||
await this.workflow.start({
|
|
||||||
type: 'order.create@1', businessKey: input.orderNo,
|
|
||||||
steps: [{key:'reserve',action:SaasActions.Demo.GoodsReserveStock,
|
|
||||||
input:{goodsId:input.goodsId,quantity:input.quantity},
|
|
||||||
compensate:{action:SaasActions.Demo.GoodsReleaseStock,input:{goodsId:input.goodsId,quantity:input.quantity,orderNo:input.orderNo}}}],
|
|
||||||
terminal: Crud.Orders,
|
|
||||||
outcomes: {
|
|
||||||
completed:{id:order.id,status:'confirmed'},
|
|
||||||
failed:{id:order.id,status:'rejected'},
|
|
||||||
compensated:{id:order.id,status:'failed'},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -117,42 +98,56 @@ export class OrdersService extends SaasCrudServiceBase<typeof Crud.Orders> {
|
|||||||
async cancelOrder(
|
async cancelOrder(
|
||||||
input: OrdersCancelOrderInput
|
input: OrdersCancelOrderInput
|
||||||
): Promise<OrdersCancelOrderOutput> {
|
): Promise<OrdersCancelOrderOutput> {
|
||||||
const order = await this.orders.lock({id:String(input.id)});
|
const order = await this.requireOrder(input.id);
|
||||||
if (!order) throw new SaasBusinessReject('ORDER_NOT_FOUND', `订单不存在: ${input.id}`);
|
|
||||||
if (order.status === 'cancelled') {
|
if (order.status === 'cancelled') {
|
||||||
return { id: input.id, status: 'cancelled', restoredStock: 0 };
|
return { id: input.id, status: 'cancelled', restoredStock: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (order.status === 'cancel_pending') return {id:input.id,status:'cancel_pending',restoredStock:0};
|
|
||||||
if (order.status !== 'confirmed') throw new SaasBusinessReject('ORDER_STATE_CONFLICT','订单当前状态不能取消;历史订单需要先核对预留记录');
|
|
||||||
const goodsId = safeInteger(order.goods_id, '商品 ID');
|
const goodsId = safeInteger(order.goods_id, '商品 ID');
|
||||||
await this.orders.update({id:order.id,status:'cancel_pending'});
|
await this.releaseStock(
|
||||||
await this.workflow.start({
|
{
|
||||||
type:'order.cancel@1',businessKey:String(order.id),
|
goodsId,
|
||||||
steps:[{key:'release',action:SaasActions.Demo.GoodsReleaseStock,input:{goodsId,quantity:order.quantity,orderNo:order.order_no}}],
|
quantity: order.quantity,
|
||||||
terminal:Crud.Orders,
|
orderNo: order.order_no,
|
||||||
outcomes:{completed:{id:order.id,status:'cancelled'},failed:{id:order.id,status:'manual_review'},compensated:{id:order.id,status:'manual_review'}},
|
},
|
||||||
|
{ idempotencyKey: businessKey('cancel', String(order.id)) }
|
||||||
|
);
|
||||||
|
|
||||||
|
const update = await this.orders.update({
|
||||||
|
id: order.id,
|
||||||
|
status: 'cancelled',
|
||||||
});
|
});
|
||||||
return {id:input.id,status:'cancel_pending',restoredStock:0};
|
if (update.affected === 0) {
|
||||||
|
return { id: input.id, status: 'cancelled', restoredStock: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: input.id,
|
||||||
|
status: 'cancelled',
|
||||||
|
restoredStock: order.quantity,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async requireOrder(id: number) {
|
private async requireOrder(id: number) {
|
||||||
// The locked info contract requires an object; its empty result would fail
|
const order = await this.orders.info({ id: String(id) });
|
||||||
// output validation before business code can classify a missing order.
|
if (!order) throw conflict(`订单不存在: ${id}`);
|
||||||
// The existing list contract permits an empty array and this exact ID filter.
|
|
||||||
const [order] = await this.orders.list({ where: { id: String(id) }, take: 1 });
|
|
||||||
if (!order) throw new SaasBusinessReject('ORDER_NOT_FOUND', `订单不存在: ${id}`);
|
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async requireGoods(goodsId: number): Promise<GoodsRecord> {
|
private async requireGoods(goodsId: number): Promise<GoodsRecord> {
|
||||||
const result = await this.batchInfo({ ids: [goodsId] });
|
const result = await this.batchInfo({ ids: [goodsId] });
|
||||||
const goods = result.list.find(item => String(item.id) === String(goodsId));
|
const goods = result.list.find(item => String(item.id) === String(goodsId));
|
||||||
if (!goods) throw new SaasBusinessReject('GOODS_NOT_FOUND', `商品不存在: ${goodsId}`);
|
if (!goods) throw conflict(`商品不存在: ${goodsId}`);
|
||||||
return goods;
|
return goods;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function businessKey(operation: string, identity: string): string {
|
||||||
|
return createHash('sha256')
|
||||||
|
.update(`${operation}:${identity}`, 'utf8')
|
||||||
|
.digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
function decimal(value: unknown, label: string): string {
|
function decimal(value: unknown, label: string): string {
|
||||||
const normalized = String(value ?? '').trim();
|
const normalized = String(value ?? '').trim();
|
||||||
if (!/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/.test(normalized)) {
|
if (!/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/.test(normalized)) {
|
||||||
|
|||||||
186
start-stable.js
Normal file
186
start-stable.js
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Starts an already-built, frozen directory. There is intentionally no build,
|
||||||
|
// codegen, Git checkout, publish, or fallback-to-RC operation in this entrypoint.
|
||||||
|
const { createHash } = require('node:crypto');
|
||||||
|
const { readdirSync, lstatSync, readFileSync } = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
const { spawnSync } = require('node:child_process');
|
||||||
|
|
||||||
|
const SHA256 = /^[a-f0-9]{64}$/;
|
||||||
|
const COMMIT = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/;
|
||||||
|
const STABLE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
||||||
|
|
||||||
|
function ensure(condition, message) {
|
||||||
|
if (!condition) throw new Error(`Stable startup refused: ${message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canonicalJson(value) {
|
||||||
|
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;
|
||||||
|
if (value && typeof value === 'object') return `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(',')}}`;
|
||||||
|
return JSON.stringify(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function candidateLockChecksum(lock) {
|
||||||
|
ensure(Object.keys(lock || {}).sort().join(',') === 'branchName,buildChecksum,buildRevision,candidateVersion,moduleCode,schemaInputChecksum', 'invalid Candidate lock fields');
|
||||||
|
return createHash('sha256').update(canonicalJson(lock), 'utf8').digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateCompiledArtifactDigest(projectDir) {
|
||||||
|
const root = path.resolve(projectDir, 'dist');
|
||||||
|
ensure(lstatSync(root, { throwIfNoEntry: false })?.isDirectory(), 'dist does not exist; deploy the frozen build, do not rebuild during startup');
|
||||||
|
const files = [];
|
||||||
|
function visit(directory) {
|
||||||
|
for (const name of readdirSync(directory)) {
|
||||||
|
const file = path.join(directory, name);
|
||||||
|
const stat = lstatSync(file);
|
||||||
|
ensure(!stat.isSymbolicLink(), 'compiled artifact must not contain symbolic links');
|
||||||
|
if (stat.isDirectory()) visit(file);
|
||||||
|
else {
|
||||||
|
ensure(stat.isFile(), 'compiled artifact contains a non-file entry');
|
||||||
|
files.push([path.relative(root, file).replace(/\\/g, '/'), file]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visit(root);
|
||||||
|
ensure(files.length > 0, 'compiled artifact is empty');
|
||||||
|
// Exact algorithm used by cool saas-model finalize with artifactDir: dist.
|
||||||
|
const hash = createHash('sha256').update('COOL_SAAS_ARTIFACT_DIRECTORY_V1\0', 'utf8');
|
||||||
|
for (const [name, file] of files.sort(([left], [right]) => left.localeCompare(right))) {
|
||||||
|
const content = readFileSync(file);
|
||||||
|
hash.update(`${Buffer.byteLength(name, 'utf8')}:`, 'utf8');
|
||||||
|
hash.update(name, 'utf8');
|
||||||
|
hash.update(`:${content.length}:`, 'utf8');
|
||||||
|
hash.update(content);
|
||||||
|
}
|
||||||
|
return `sha256:${hash.digest('hex')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sourceCommit(projectDir, environment) {
|
||||||
|
const gitOptions = { cwd: projectDir, encoding: 'utf8', windowsHide: true, timeout: 30_000 };
|
||||||
|
const root = spawnSync('git', ['rev-parse', '--show-toplevel'], gitOptions);
|
||||||
|
if (root.status === 0) {
|
||||||
|
ensure(path.resolve(root.stdout.trim()) === path.resolve(projectDir), 'project must be its own exact Git checkout');
|
||||||
|
const status = spawnSync('git', ['status', '--porcelain=v1', '--untracked-files=all'], gitOptions);
|
||||||
|
ensure(status.status === 0 && !status.stdout.trim(), 'Git checkout is dirty');
|
||||||
|
const commit = spawnSync('git', ['rev-parse', 'HEAD'], gitOptions);
|
||||||
|
ensure(commit.status === 0 && COMMIT.test(commit.stdout.trim()), 'Git HEAD is unavailable');
|
||||||
|
ensure(!environment.BUILD_SHA || environment.BUILD_SHA === commit.stdout.trim(), 'BUILD_SHA differs from Git HEAD');
|
||||||
|
return commit.stdout.trim();
|
||||||
|
}
|
||||||
|
ensure(!lstatSync(path.join(projectDir, '.git'), { throwIfNoEntry: false }), 'Git metadata exists but could not be verified');
|
||||||
|
// Git-free deployment archives must carry the trusted orchestrator's commit.
|
||||||
|
// This is cross-checked against both the authoritative Frozen Artifact and
|
||||||
|
// its actual compiled bytes, never accepted as a digest substitute.
|
||||||
|
ensure(COMMIT.test(environment.BUILD_SHA || ''), 'Git-free deployments require an exact BUILD_SHA');
|
||||||
|
return environment.BUILD_SHA;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchReleaseEvidence(environment, fetcher = fetch) {
|
||||||
|
const releaseId = Number(environment.RPC_RELEASE_ID);
|
||||||
|
ensure(Number.isSafeInteger(releaseId) && releaseId > 0, 'RPC_RELEASE_ID must select a Stable release');
|
||||||
|
const base = new URL(environment.SAAS_CONTROL_PLANE_URL || environment.COOL_SAAS_CONTROL_PLANE_URL || 'http://127.0.0.1:8080');
|
||||||
|
const local = ['local', 'test'].includes(environment.NODE_ENV);
|
||||||
|
ensure(base.protocol === 'https:' || (local && base.protocol === 'http:' && ['127.0.0.1', 'localhost', '[::1]'].includes(base.hostname)), 'use HTTPS (loopback HTTP is local/test only)');
|
||||||
|
ensure(!base.username && !base.password, 'control-plane URL must not contain credentials');
|
||||||
|
ensure(String(environment.COOL_SAAS_TOKEN || '').trim(), 'a short-lived CLI/CI Bearer credential is required to verify immutable release evidence');
|
||||||
|
const url = new URL('/admin/saas/module_release/info', base);
|
||||||
|
url.searchParams.set('id', String(releaseId));
|
||||||
|
const response = await fetcher(url, {
|
||||||
|
method: 'GET', headers: { Authorization: `Bearer ${environment.COOL_SAAS_TOKEN}` },
|
||||||
|
redirect: 'error', signal: AbortSignal.timeout(10_000)
|
||||||
|
});
|
||||||
|
ensure(response.ok, `control plane rejected release evidence (${response.status})`);
|
||||||
|
const body = await response.json();
|
||||||
|
ensure(body?.code === 1000 && body.data, 'control plane did not return release evidence');
|
||||||
|
return body.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyStableEvidence({ release, lock, generated, digest, commit, releaseId }) {
|
||||||
|
const build = release?.build;
|
||||||
|
const artifact = release?.frozenArtifact;
|
||||||
|
ensure(release?.id === Number(releaseId) && release.releaseType === 'stable' && release.status === 'published' && release.lifecycleVersion === 1, 'target is not a published lifecycle Stable release');
|
||||||
|
ensure(STABLE.test(release.version || '') && release.serviceVersion === release.version, 'Stable version identity is invalid');
|
||||||
|
ensure(build && artifact && Number(release.promotedFromReleaseId) === Number(artifact.rcReleaseId) && Number(release.frozenArtifactId) === Number(artifact.id), 'Stable does not reference its source RC frozen artifact');
|
||||||
|
ensure(Number(build.id) === Number(release.buildSnapshotId) && Number(build.moduleId) === Number(release.moduleId) && Number(artifact.moduleId) === Number(release.moduleId), 'Build/module/artifact association differs');
|
||||||
|
ensure(Number(build.revision) === Number(lock.buildRevision) && Number(release.buildRevision) === Number(lock.buildRevision) && Number(artifact.buildRevision) === Number(lock.buildRevision), 'Build Revision differs from the Candidate lock');
|
||||||
|
ensure(SHA256.test(build.buildChecksum || '') && build.buildChecksum === lock.buildChecksum && build.schemaInputChecksum === lock.schemaInputChecksum, 'Build checksums differ from the Candidate lock');
|
||||||
|
ensure(lock.candidateVersion === `${release.version}-rc.${lock.buildRevision}` && lock.branchName === `release/${lock.candidateVersion}` && artifact.sourceBranch === lock.branchName && artifact.tagName === `v${lock.candidateVersion}`, 'source Candidate branch/version/tag differs');
|
||||||
|
ensure(candidateLockChecksum(lock) === artifact.generatedLockChecksum, 'generated Candidate lock checksum differs');
|
||||||
|
ensure(COMMIT.test(artifact.sourceCommit || '') && artifact.sourceCommit === commit, 'source commit differs from the Frozen Artifact');
|
||||||
|
ensure(/^sha256:[a-f0-9]{64}$/.test(artifact.artifactDigest || ''), 'Frozen Artifact has no compiled digest; old Git-only evidence cannot be upgraded or forged here');
|
||||||
|
ensure(artifact.artifactDigest === digest, 'compiled artifact digest differs from the Frozen Artifact');
|
||||||
|
ensure(SHA256.test(artifact.artifactIdentityChecksum || '') && SHA256.test(artifact.implementationManifestChecksum || ''), 'frozen implementation identity is incomplete');
|
||||||
|
ensure(generated.moduleCode === lock.moduleCode && generated.revision === lock.buildRevision && generated.schemaInputChecksum === lock.schemaInputChecksum, 'compiled generated identity differs from the immutable Build');
|
||||||
|
ensure(!generated.releaseVersion || generated.releaseVersion === release.version, 'generated runtime embeds a different Release version');
|
||||||
|
ensure(!generated.implementationManifestChecksum || generated.implementationManifestChecksum === artifact.implementationManifestChecksum, 'compiled implementation checksum differs');
|
||||||
|
const manifest = release.schemaSnapshot?.actionManifest;
|
||||||
|
const contracts = release.schemaSnapshot?.runtimeContracts;
|
||||||
|
ensure(manifest && contracts?.implementationManifestChecksum === artifact.implementationManifestChecksum && manifest.artifactDigest === artifact.artifactDigest, 'Stable release envelope differs from the frozen implementation/artifact');
|
||||||
|
ensure(generated.runtimeContractChecksum === contracts.runtimeContractChecksum && SHA256.test(contracts.runtimeContractChecksum || ''), 'compiled Runtime contract checksum differs');
|
||||||
|
ensure(generated.schemaVersion === release.schemaVersion && build.schemaVersion === release.schemaVersion, 'compiled Schema version differs');
|
||||||
|
return {
|
||||||
|
SAAS_RUNTIME_MODE: 'production', RPC_REQUIRE_EXACT_RELEASE: 'true',
|
||||||
|
RPC_MODULE_CODE: lock.moduleCode, RPC_RELEASE_ID: String(release.id),
|
||||||
|
RPC_RELEASE_VERSION: release.version, RPC_SERVICE_VERSION: release.serviceVersion,
|
||||||
|
SAAS_BUILD_REVISION: String(build.revision), SAAS_BUILD_CHECKSUM: build.buildChecksum,
|
||||||
|
SAAS_SCHEMA_INPUT_CHECKSUM: build.schemaInputChecksum,
|
||||||
|
RPC_MIN_SCHEMA_VERSION: String(release.schemaVersion), RPC_MAX_SCHEMA_VERSION: String(release.schemaVersion),
|
||||||
|
RPC_SCHEMA_CHECKSUM: release.schemaChecksum,
|
||||||
|
RPC_ACTION_MANIFEST_CHECKSUM: release.schemaSnapshot.actionManifestChecksum,
|
||||||
|
RPC_RUNTIME_CONTRACT_CHECKSUM: contracts.runtimeContractChecksum,
|
||||||
|
RPC_IMPLEMENTATION_MANIFEST_CHECKSUM: artifact.implementationManifestChecksum,
|
||||||
|
RPC_ARTIFACT_DIGEST: artifact.artifactDigest,
|
||||||
|
RPC_ARTIFACT_IDENTITY_CHECKSUM: artifact.artifactIdentityChecksum,
|
||||||
|
BUILD_SHA: artifact.sourceCommit
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyExactEnvironment(environment, exact) {
|
||||||
|
for (const [key, value] of Object.entries(exact)) {
|
||||||
|
ensure(value != null && String(value).length > 0, `authoritative field ${key} is missing`);
|
||||||
|
ensure(!environment[key] || environment[key] === String(value), `${key} conflicts with authoritative Stable evidence`);
|
||||||
|
environment[key] = String(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyBootstrapSession(session, exact) {
|
||||||
|
ensure(session?.moduleCode === exact.RPC_MODULE_CODE && session.revision === Number(exact.SAAS_BUILD_REVISION), 'bootstrap module/Revision differs');
|
||||||
|
const mapping = {
|
||||||
|
releaseId: 'RPC_RELEASE_ID', releaseVersion: 'RPC_RELEASE_VERSION', serviceVersion: 'RPC_SERVICE_VERSION',
|
||||||
|
runtimeMode: 'SAAS_RUNTIME_MODE', buildRevision: 'SAAS_BUILD_REVISION', buildChecksum: 'SAAS_BUILD_CHECKSUM',
|
||||||
|
schemaInputChecksum: 'SAAS_SCHEMA_INPUT_CHECKSUM', implementationManifestChecksum: 'RPC_IMPLEMENTATION_MANIFEST_CHECKSUM',
|
||||||
|
artifactDigest: 'RPC_ARTIFACT_DIGEST', artifactIdentityChecksum: 'RPC_ARTIFACT_IDENTITY_CHECKSUM'
|
||||||
|
};
|
||||||
|
for (const [field, key] of Object.entries(mapping)) ensure(String(session.release?.[field]) === exact[key], `authenticated bootstrap ${field} differs from Stable evidence`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startStable(options = {}) {
|
||||||
|
const projectDir = path.resolve(options.projectDir || __dirname);
|
||||||
|
const environment = options.environment || process.env;
|
||||||
|
const release = await fetchReleaseEvidence(environment, options.fetch);
|
||||||
|
const lock = JSON.parse(readFileSync(path.join(projectDir, '.cool-saas-candidate.json'), 'utf8'));
|
||||||
|
const digest = calculateCompiledArtifactDigest(projectDir);
|
||||||
|
const commit = sourceCommit(projectDir, environment);
|
||||||
|
// Do not execute generated JavaScript before its compiled digest is verified.
|
||||||
|
ensure(digest === release.frozenArtifact?.artifactDigest, 'compiled artifact digest differs from the Frozen Artifact');
|
||||||
|
const generated = (options.loadGenerated || (() => require(path.join(projectDir, 'dist/saas/generated')).GeneratedSaasModule))();
|
||||||
|
const exact = verifyStableEvidence({ release, lock, generated, digest, commit, releaseId: environment.RPC_RELEASE_ID });
|
||||||
|
applyExactEnvironment(environment, exact);
|
||||||
|
environment.SAAS_CONTROL_PLANE_URL ||= environment.COOL_SAAS_CONTROL_PLANE_URL || 'http://127.0.0.1:8080';
|
||||||
|
delete environment.COOL_SAAS_TOKEN;
|
||||||
|
delete environment.NODE_AUTH_TOKEN;
|
||||||
|
delete environment.npm_config__authToken;
|
||||||
|
const start = options.start || require(path.join(projectDir, 'bootstrap.js')).start;
|
||||||
|
return start({ verifySession: session => {
|
||||||
|
verifyBootstrapSession(session, exact);
|
||||||
|
ensure(calculateCompiledArtifactDigest(projectDir) === digest, 'compiled bytes changed during bootstrap');
|
||||||
|
} });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require.main === module) startStable().catch(error => {
|
||||||
|
process.stderr.write(`${error?.message || error}\n`);
|
||||||
|
process.exitCode = 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = { startStable, verifyStableEvidence, verifyBootstrapSession, applyExactEnvironment, calculateCompiledArtifactDigest, candidateLockChecksum, fetchReleaseEvidence, sourceCommit };
|
||||||
195
test/start-stable.test.cjs
Normal file
195
test/start-stable.test.cjs
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const test = require('node:test');
|
||||||
|
const { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, symlinkSync } = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
const os = require('node:os');
|
||||||
|
const { spawnSync } = require('node:child_process');
|
||||||
|
const { createHash } = require('node:crypto');
|
||||||
|
const {
|
||||||
|
startStable, verifyStableEvidence, verifyBootstrapSession, applyExactEnvironment,
|
||||||
|
calculateCompiledArtifactDigest, candidateLockChecksum, fetchReleaseEvidence, sourceCommit
|
||||||
|
} = require('../start-stable');
|
||||||
|
|
||||||
|
function fixture() {
|
||||||
|
const lock = { moduleCode: 'demo', candidateVersion: '1.0.1-rc.2', buildRevision: 2, buildChecksum: 'b'.repeat(64), schemaInputChecksum: 'c'.repeat(64), branchName: 'release/1.0.1-rc.2' };
|
||||||
|
const generated = { moduleCode: 'demo', revision: 2, schemaVersion: 3, schemaInputChecksum: lock.schemaInputChecksum, implementationManifestChecksum: 'd'.repeat(64), runtimeContractChecksum: 'e'.repeat(64) };
|
||||||
|
const commit = 'a'.repeat(40);
|
||||||
|
const digest = `sha256:${'f'.repeat(64)}`;
|
||||||
|
const release = {
|
||||||
|
id: 42, moduleId: 2, releaseType: 'stable', status: 'published', lifecycleVersion: 1,
|
||||||
|
version: '1.0.1', serviceVersion: '1.0.1', promotedFromReleaseId: 41, buildSnapshotId: 52,
|
||||||
|
buildRevision: 2, frozenArtifactId: 62, schemaVersion: 3, schemaChecksum: '3'.repeat(64),
|
||||||
|
build: { id: 52, moduleId: 2, revision: 2, schemaVersion: 3, buildChecksum: lock.buildChecksum, schemaInputChecksum: lock.schemaInputChecksum },
|
||||||
|
frozenArtifact: { id: 62, rcReleaseId: 41, moduleId: 2, buildRevision: 2, sourceBranch: lock.branchName, tagName: `v${lock.candidateVersion}`, sourceCommit: commit, generatedLockChecksum: candidateLockChecksum(lock), artifactDigest: digest, artifactIdentityChecksum: '1'.repeat(64), implementationManifestChecksum: generated.implementationManifestChecksum },
|
||||||
|
schemaSnapshot: { actionManifest: { artifactDigest: digest }, actionManifestChecksum: '2'.repeat(64), runtimeContracts: { implementationManifestChecksum: generated.implementationManifestChecksum, runtimeContractChecksum: generated.runtimeContractChecksum } }
|
||||||
|
};
|
||||||
|
return { release, lock, generated, digest, commit, releaseId: 42 };
|
||||||
|
}
|
||||||
|
|
||||||
|
function sessionFor(exact) {
|
||||||
|
return { moduleCode: exact.RPC_MODULE_CODE, revision: Number(exact.SAAS_BUILD_REVISION), release: {
|
||||||
|
releaseId: Number(exact.RPC_RELEASE_ID), releaseVersion: exact.RPC_RELEASE_VERSION, serviceVersion: exact.RPC_SERVICE_VERSION,
|
||||||
|
runtimeMode: exact.SAAS_RUNTIME_MODE, buildRevision: Number(exact.SAAS_BUILD_REVISION), buildChecksum: exact.SAAS_BUILD_CHECKSUM,
|
||||||
|
schemaInputChecksum: exact.SAAS_SCHEMA_INPUT_CHECKSUM, implementationManifestChecksum: exact.RPC_IMPLEMENTATION_MANIFEST_CHECKSUM,
|
||||||
|
artifactDigest: exact.RPC_ARTIFACT_DIGEST, artifactIdentityChecksum: exact.RPC_ARTIFACT_IDENTITY_CHECKSUM
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
test('Stable derives its own deployment envelope from the same frozen Candidate and Build', () => {
|
||||||
|
const input = fixture();
|
||||||
|
const before = JSON.stringify(input);
|
||||||
|
const exact = verifyStableEvidence(input);
|
||||||
|
assert.equal(exact.RPC_RELEASE_ID, '42');
|
||||||
|
assert.equal(exact.RPC_RELEASE_VERSION, '1.0.1');
|
||||||
|
assert.equal(exact.SAAS_RUNTIME_MODE, 'production');
|
||||||
|
assert.equal(exact.SAAS_BUILD_REVISION, '2');
|
||||||
|
assert.equal(exact.BUILD_SHA, input.commit);
|
||||||
|
assert.equal(exact.RPC_ARTIFACT_DIGEST, input.digest);
|
||||||
|
assert.equal(JSON.stringify(input), before, 'must never rewrite the Candidate or frozen evidence');
|
||||||
|
verifyBootstrapSession(sessionFor(exact), exact);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fail closed on RC targets, history-only artifacts, or any frozen identity mismatch', () => {
|
||||||
|
const edits = [
|
||||||
|
data => { data.release.releaseType = 'rc'; },
|
||||||
|
data => { data.release.status = 'retired'; },
|
||||||
|
data => { data.release.version = '1.0.1-rc.2'; },
|
||||||
|
data => { data.release.frozenArtifact.artifactDigest = null; },
|
||||||
|
data => { data.release.frozenArtifact.rcReleaseId++; },
|
||||||
|
data => { data.release.buildSnapshotId++; },
|
||||||
|
data => { data.release.build.buildChecksum = '9'.repeat(64); },
|
||||||
|
data => { data.release.buildRevision++; },
|
||||||
|
data => { data.release.frozenArtifact.generatedLockChecksum = '9'.repeat(64); },
|
||||||
|
data => { data.release.frozenArtifact.tagName = 'v9.0.0'; },
|
||||||
|
data => { data.commit = '9'.repeat(40); },
|
||||||
|
data => { data.digest = `sha256:${'9'.repeat(64)}`; },
|
||||||
|
data => { data.generated.revision++; },
|
||||||
|
data => { data.generated.releaseVersion = '1.0.1-rc.2'; },
|
||||||
|
data => { data.generated.implementationManifestChecksum = '9'.repeat(64); },
|
||||||
|
data => { data.generated.runtimeContractChecksum = '9'.repeat(64); },
|
||||||
|
data => { data.generated.schemaVersion++; },
|
||||||
|
data => { data.release.schemaSnapshot.actionManifest.artifactDigest = `sha256:${'9'.repeat(64)}`; }
|
||||||
|
];
|
||||||
|
for (const edit of edits) {
|
||||||
|
const input = fixture();
|
||||||
|
edit(input);
|
||||||
|
assert.throws(() => verifyStableEvidence(input), /Stable startup refused/);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('environment and authenticated bootstrap cannot silently replace Stable with RC or a different Build', () => {
|
||||||
|
const exact = verifyStableEvidence(fixture());
|
||||||
|
assert.throws(() => applyExactEnvironment({ RPC_RELEASE_ID: '41' }, exact), /conflicts/);
|
||||||
|
const environment = {};
|
||||||
|
applyExactEnvironment(environment, exact);
|
||||||
|
assert.equal(environment.RPC_ARTIFACT_DIGEST, exact.RPC_ARTIFACT_DIGEST);
|
||||||
|
for (const key of ['releaseId', 'releaseVersion', 'buildChecksum', 'runtimeMode', 'artifactDigest', 'artifactIdentityChecksum']) {
|
||||||
|
const session = sessionFor(exact);
|
||||||
|
session.release[key] = 'incorrect';
|
||||||
|
assert.throws(() => verifyBootstrapSession(session, exact), /differs/);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('compiled digest uses the exact finalize byte format and detects changes', t => {
|
||||||
|
const root = mkdtempSync(path.join(os.tmpdir(), 'stable-digest-test-'));
|
||||||
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
||||||
|
mkdirSync(path.join(root, 'dist/nested'), { recursive: true });
|
||||||
|
writeFileSync(path.join(root, 'dist/a.js'), 'first');
|
||||||
|
writeFileSync(path.join(root, 'dist/nested/b.js'), 'second');
|
||||||
|
const exactHash = createHash('sha256').update('COOL_SAAS_ARTIFACT_DIRECTORY_V1\0', 'utf8');
|
||||||
|
for (const [name, content] of [['a.js', 'first'], ['nested/b.js', 'second']]) {
|
||||||
|
exactHash.update(`${Buffer.byteLength(name)}:`).update(name).update(`:${Buffer.byteLength(content)}:`).update(content);
|
||||||
|
}
|
||||||
|
assert.equal(calculateCompiledArtifactDigest(root), `sha256:${exactHash.digest('hex')}`);
|
||||||
|
const oldDigest = calculateCompiledArtifactDigest(root);
|
||||||
|
writeFileSync(path.join(root, 'dist/a.js'), 'changed');
|
||||||
|
assert.notEqual(calculateCompiledArtifactDigest(root), oldDigest);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('missing, empty, and linked compiled directories are refused', t => {
|
||||||
|
const root = mkdtempSync(path.join(os.tmpdir(), 'stable-linked-test-'));
|
||||||
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
||||||
|
assert.throws(() => calculateCompiledArtifactDigest(root), /does not exist/);
|
||||||
|
mkdirSync(path.join(root, 'dist'));
|
||||||
|
assert.throws(() => calculateCompiledArtifactDigest(root), /empty/);
|
||||||
|
mkdirSync(path.join(root, 'outside'));
|
||||||
|
writeFileSync(path.join(root, 'outside/escaped.js'), 'outside the frozen directory');
|
||||||
|
symlinkSync(path.join(root, 'outside'), path.join(root, 'dist/linked'), 'junction');
|
||||||
|
assert.throws(() => calculateCompiledArtifactDigest(root), /symbolic links/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('control-plane evidence requires a Bearer credential and local HTTP cannot target a remote host', async () => {
|
||||||
|
const environment = { NODE_ENV: 'local', RPC_RELEASE_ID: '42', SAAS_CONTROL_PLANE_URL: 'http://127.0.0.1:8080', COOL_SAAS_TOKEN: 'test-short-lived-credential' };
|
||||||
|
const release = fixture().release;
|
||||||
|
assert.equal(await fetchReleaseEvidence(environment, async (url, options) => {
|
||||||
|
assert.equal(url.searchParams.get('id'), '42');
|
||||||
|
assert.equal(options.headers.Authorization, `Bearer ${environment.COOL_SAAS_TOKEN}`);
|
||||||
|
assert.equal(options.redirect, 'error');
|
||||||
|
return { ok: true, json: async () => ({ code: 1000, data: release }) };
|
||||||
|
}), release);
|
||||||
|
await assert.rejects(fetchReleaseEvidence({ ...environment, COOL_SAAS_TOKEN: '' }), /credential/);
|
||||||
|
await assert.rejects(fetchReleaseEvidence({ ...environment, SAAS_CONTROL_PLANE_URL: 'http://example.com' }), /HTTPS/);
|
||||||
|
await assert.rejects(fetchReleaseEvidence(environment, async () => ({ ok: false, status: 401 })), /rejected/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('actual startup verifies existing bytes, removes CLI secrets, and never invokes a build', async t => {
|
||||||
|
const root = mkdtempSync(path.join(os.tmpdir(), 'stable-start-test-'));
|
||||||
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
||||||
|
mkdirSync(path.join(root, 'dist'), { recursive: true });
|
||||||
|
writeFileSync(path.join(root, 'dist/configuration.js'), 'frozen compiled bytes');
|
||||||
|
const data = fixture();
|
||||||
|
data.digest = calculateCompiledArtifactDigest(root);
|
||||||
|
data.release.frozenArtifact.artifactDigest = data.digest;
|
||||||
|
data.release.schemaSnapshot.actionManifest.artifactDigest = data.digest;
|
||||||
|
writeFileSync(path.join(root, '.cool-saas-candidate.json'), JSON.stringify(data.lock));
|
||||||
|
const environment = { NODE_ENV: 'local', RPC_RELEASE_ID: '42', BUILD_SHA: data.commit, COOL_SAAS_TOKEN: 'short-lived-test', NODE_AUTH_TOKEN: 'must-not-inherit' };
|
||||||
|
let starts = 0;
|
||||||
|
const options = {
|
||||||
|
projectDir: root, environment,
|
||||||
|
fetch: async () => ({ ok: true, json: async () => ({ code: 1000, data: data.release }) }),
|
||||||
|
loadGenerated: () => data.generated,
|
||||||
|
start: ({ verifySession }) => {
|
||||||
|
starts++;
|
||||||
|
assert.equal(environment.COOL_SAAS_TOKEN, undefined);
|
||||||
|
assert.equal(environment.NODE_AUTH_TOKEN, undefined);
|
||||||
|
verifySession(sessionFor(environment));
|
||||||
|
return 'started';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
assert.equal(await startStable(options), 'started');
|
||||||
|
assert.equal(starts, 1);
|
||||||
|
assert.equal(readFileSync(path.join(root, 'dist/configuration.js'), 'utf8'), 'frozen compiled bytes');
|
||||||
|
writeFileSync(path.join(root, 'dist/configuration.js'), 'tampered');
|
||||||
|
environment.COOL_SAAS_TOKEN = 'short-lived-test';
|
||||||
|
options.loadGenerated = () => { throw new Error('must not execute unverified compiled code'); };
|
||||||
|
await assert.rejects(startStable(options), /compiled artifact digest differs/);
|
||||||
|
assert.equal(starts, 1);
|
||||||
|
writeFileSync(path.join(root, 'dist/configuration.js'), 'frozen compiled bytes');
|
||||||
|
environment.COOL_SAAS_TOKEN = 'short-lived-test';
|
||||||
|
await assert.rejects(startStable({
|
||||||
|
...options,
|
||||||
|
loadGenerated: () => data.generated,
|
||||||
|
start: ({ verifySession }) => {
|
||||||
|
writeFileSync(path.join(root, 'dist/configuration.js'), 'changed during bootstrap');
|
||||||
|
verifySession(sessionFor(environment));
|
||||||
|
}
|
||||||
|
}), /changed during bootstrap/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dirty Git checkouts cannot be presented as the frozen source commit', t => {
|
||||||
|
const root = mkdtempSync(path.join(os.tmpdir(), 'stable-git-test-'));
|
||||||
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
||||||
|
const git = (...args) => {
|
||||||
|
const result = spawnSync('git', args, { cwd: root, encoding: 'utf8', windowsHide: true });
|
||||||
|
assert.equal(result.status, 0, result.stderr);
|
||||||
|
return result.stdout.trim();
|
||||||
|
};
|
||||||
|
git('init'); git('config', 'user.email', 'test@example.invalid'); git('config', 'user.name', 'Stable Test');
|
||||||
|
writeFileSync(path.join(root, 'business.js'), 'original');
|
||||||
|
git('add', '.'); git('commit', '-m', 'frozen');
|
||||||
|
assert.equal(sourceCommit(root, {}), git('rev-parse', 'HEAD'));
|
||||||
|
writeFileSync(path.join(root, 'business.js'), 'modified');
|
||||||
|
assert.throws(() => sourceCommit(root, {}), /dirty/);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user