@yss123/cage-cli 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +18 -2
- package/bin/cage.js +6 -0
- package/bin/create-cage-app.js +6 -0
- package/dist/analyze.d.ts +18 -0
- package/dist/analyze.js +26 -0
- package/dist/analyze.js.map +1 -0
- package/dist/baseline.d.ts +5 -0
- package/dist/baseline.js +50 -0
- package/dist/baseline.js.map +1 -0
- package/dist/budget.d.ts +7 -0
- package/dist/budget.js +52 -0
- package/dist/budget.js.map +1 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.js +47 -0
- package/dist/config.js.map +1 -0
- package/dist/create.d.ts +1 -0
- package/dist/create.js +47 -0
- package/dist/create.js.map +1 -0
- package/dist/database.d.ts +5 -0
- package/dist/database.js +70 -0
- package/dist/database.js.map +1 -0
- package/dist/derive.d.ts +14 -0
- package/dist/derive.js +163 -0
- package/dist/derive.js.map +1 -0
- package/dist/dev.d.ts +2 -0
- package/dist/dev.js +155 -0
- package/dist/dev.js.map +1 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.js +15 -0
- package/dist/env.js.map +1 -0
- package/dist/findings.d.ts +11 -0
- package/dist/findings.js +13 -0
- package/dist/findings.js.map +1 -0
- package/dist/gen/contracts.d.ts +6 -0
- package/dist/gen/contracts.js +167 -0
- package/dist/gen/contracts.js.map +1 -0
- package/dist/gen/index.d.ts +19 -0
- package/dist/gen/index.js +137 -0
- package/dist/gen/index.js.map +1 -0
- package/dist/gen/zod-code.d.ts +7 -0
- package/dist/gen/zod-code.js +161 -0
- package/dist/gen/zod-code.js.map +1 -0
- package/dist/graph-snapshot.d.ts +13 -0
- package/dist/graph-snapshot.js +46 -0
- package/dist/graph-snapshot.js.map +1 -0
- package/dist/graph.d.ts +11 -0
- package/dist/graph.js +87 -0
- package/dist/graph.js.map +1 -0
- package/dist/impact.d.ts +28 -0
- package/dist/impact.js +223 -0
- package/dist/impact.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/load.d.ts +6 -0
- package/dist/load.js +17 -0
- package/dist/load.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +176 -0
- package/dist/main.js.map +1 -0
- package/dist/migrate.d.ts +7 -0
- package/dist/migrate.js +69 -0
- package/dist/migrate.js.map +1 -0
- package/dist/ontology.d.ts +41 -0
- package/dist/ontology.js +270 -0
- package/dist/ontology.js.map +1 -0
- package/dist/process.d.ts +6 -0
- package/dist/process.js +39 -0
- package/dist/process.js.map +1 -0
- package/dist/rules.d.ts +23 -0
- package/dist/rules.js +251 -0
- package/dist/rules.js.map +1 -0
- package/dist/static-check.d.ts +23 -0
- package/dist/static-check.js +225 -0
- package/dist/static-check.js.map +1 -0
- package/dist/sync.d.ts +5 -0
- package/dist/sync.js +46 -0
- package/dist/sync.js.map +1 -0
- package/dist/upgrade.d.ts +10 -0
- package/dist/upgrade.js +81 -0
- package/dist/upgrade.js.map +1 -0
- package/dist/verify.d.ts +7 -0
- package/dist/verify.js +88 -0
- package/dist/verify.js.map +1 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/graph-ui/app.js +1065 -0
- package/graph-ui/index.html +197 -0
- package/graph-ui/model.js +141 -0
- package/graph-ui/relations.js +125 -0
- package/graph-ui/structure.js +74 -0
- package/graph-ui/style.css +1698 -0
- package/managed/errors/CAGE-APPR-001.md +32 -0
- package/managed/errors/CAGE-APPR-002.md +32 -0
- package/managed/errors/CAGE-APPR-003.md +32 -0
- package/managed/errors/CAGE-APPR-004.md +32 -0
- package/managed/errors/CAGE-APPR-005.md +32 -0
- package/managed/errors/CAGE-APPR-006.md +32 -0
- package/managed/errors/CAGE-APPR-007.md +32 -0
- package/managed/errors/CAGE-APPR-010.md +32 -0
- package/managed/errors/CAGE-AUTH-001.md +32 -0
- package/managed/errors/CAGE-BUDGET-001.md +32 -0
- package/managed/errors/CAGE-BUDGET-002.md +32 -0
- package/managed/errors/CAGE-BUDGET-003.md +32 -0
- package/managed/errors/CAGE-CMD-002.md +32 -0
- package/managed/errors/CAGE-CMD-003.md +34 -0
- package/managed/errors/CAGE-CONN-001.md +32 -0
- package/managed/errors/CAGE-CONN-002.md +32 -0
- package/managed/errors/CAGE-CONN-003.md +32 -0
- package/managed/errors/CAGE-CONN-004.md +32 -0
- package/managed/errors/CAGE-CONN-005.md +32 -0
- package/managed/errors/CAGE-CONN-006.md +32 -0
- package/managed/errors/CAGE-CONN-007.md +32 -0
- package/managed/errors/CAGE-CONN-008.md +32 -0
- package/managed/errors/CAGE-CONN-009.md +32 -0
- package/managed/errors/CAGE-CONN-010.md +32 -0
- package/managed/errors/CAGE-CONN-011.md +32 -0
- package/managed/errors/CAGE-CONN-012.md +32 -0
- package/managed/errors/CAGE-CREATE-001.md +32 -0
- package/managed/errors/CAGE-DECL-001.md +34 -0
- package/managed/errors/CAGE-DECL-002.md +34 -0
- package/managed/errors/CAGE-DECL-003.md +34 -0
- package/managed/errors/CAGE-DECL-004.md +34 -0
- package/managed/errors/CAGE-DEP-001.md +34 -0
- package/managed/errors/CAGE-DEP-002.md +34 -0
- package/managed/errors/CAGE-DEP-003.md +34 -0
- package/managed/errors/CAGE-DEP-004.md +34 -0
- package/managed/errors/CAGE-DEP-005.md +34 -0
- package/managed/errors/CAGE-DLQ-001.md +32 -0
- package/managed/errors/CAGE-ENV-001.md +32 -0
- package/managed/errors/CAGE-EVENT-001.md +32 -0
- package/managed/errors/CAGE-EVENT-002.md +32 -0
- package/managed/errors/CAGE-FIELD-001.md +32 -0
- package/managed/errors/CAGE-FIELD-002.md +32 -0
- package/managed/errors/CAGE-FIELD-003.md +32 -0
- package/managed/errors/CAGE-FIELD-004.md +32 -0
- package/managed/errors/CAGE-GEN-001.md +33 -0
- package/managed/errors/CAGE-GEN-002.md +33 -0
- package/managed/errors/CAGE-GRANT-001.md +32 -0
- package/managed/errors/CAGE-GRANT-002.md +32 -0
- package/managed/errors/CAGE-GRANT-003.md +32 -0
- package/managed/errors/CAGE-IMPACT-001.md +32 -0
- package/managed/errors/CAGE-JOB-001.md +32 -0
- package/managed/errors/CAGE-JOB-002.md +32 -0
- package/managed/errors/CAGE-JOB-003.md +32 -0
- package/managed/errors/CAGE-LINT-001.md +32 -0
- package/managed/errors/CAGE-MIG-001.md +32 -0
- package/managed/errors/CAGE-MIG-002.md +32 -0
- package/managed/errors/CAGE-MIG-003.md +32 -0
- package/managed/errors/CAGE-MIG-004.md +32 -0
- package/managed/errors/CAGE-MIG-005.md +32 -0
- package/managed/errors/CAGE-NAME-001.md +32 -0
- package/managed/errors/CAGE-NAME-002.md +32 -0
- package/managed/errors/CAGE-NAME-003.md +32 -0
- package/managed/errors/CAGE-NAME-004.md +32 -0
- package/managed/errors/CAGE-NAME-005.md +32 -0
- package/managed/errors/CAGE-NAME-006.md +32 -0
- package/managed/errors/CAGE-ORG-001.md +32 -0
- package/managed/errors/CAGE-PAIR-002.md +33 -0
- package/managed/errors/CAGE-QUERY-001.md +32 -0
- package/managed/errors/CAGE-QUERY-002.md +32 -0
- package/managed/errors/CAGE-QUERY-003.md +32 -0
- package/managed/errors/CAGE-REACT-001.md +32 -0
- package/managed/errors/CAGE-REACT-002.md +32 -0
- package/managed/errors/CAGE-READS-001.md +32 -0
- package/managed/errors/CAGE-REG-001.md +32 -0
- package/managed/errors/CAGE-REG-002.md +32 -0
- package/managed/errors/CAGE-REG-003.md +32 -0
- package/managed/errors/CAGE-ROUTE-001.md +32 -0
- package/managed/errors/CAGE-SCOPE-001.md +32 -0
- package/managed/errors/CAGE-SCOPE-002.md +32 -0
- package/managed/errors/CAGE-SECRET-001.md +32 -0
- package/managed/errors/CAGE-SECRET-002.md +32 -0
- package/managed/errors/CAGE-SENS-001.md +32 -0
- package/managed/errors/CAGE-SQL-001.md +32 -0
- package/managed/errors/CAGE-SQL-002.md +32 -0
- package/managed/errors/CAGE-STATE-001.md +32 -0
- package/managed/errors/CAGE-STATE-002.md +32 -0
- package/managed/errors/CAGE-STATE-003.md +32 -0
- package/managed/errors/CAGE-STATE-004.md +32 -0
- package/managed/errors/CAGE-STATE-005.md +32 -0
- package/managed/errors/CAGE-STATE-010.md +32 -0
- package/managed/errors/CAGE-STATE-011.md +32 -0
- package/managed/errors/CAGE-SYNC-001.md +35 -0
- package/managed/errors/CAGE-TEST-001.md +33 -0
- package/managed/errors/CAGE-UNSAFE-001.md +32 -0
- package/managed/errors/CAGE-UPGRADE-001.md +32 -0
- package/managed/errors/README.md +99 -0
- package/package.json +60 -3
- package/templates/app/.dockerignore +10 -0
- package/templates/app/.env.example +17 -0
- package/templates/app/.github/workflows/cage.yml +44 -0
- package/templates/app/AGENTS.md +13 -0
- package/templates/app/Dockerfile +23 -0
- package/templates/app/README.md +58 -0
- package/templates/app/apps/api/package.json +16 -0
- package/templates/app/apps/api/src/main.ts +10 -0
- package/templates/app/apps/api/src/worker.ts +14 -0
- package/templates/app/apps/api/tsconfig.json +5 -0
- package/templates/app/apps/web/index.html +13 -0
- package/templates/app/apps/web/package.json +22 -0
- package/templates/app/apps/web/src/main.tsx +552 -0
- package/templates/app/apps/web/src/style.css +663 -0
- package/templates/app/apps/web/tsconfig.json +11 -0
- package/templates/app/apps/web/vite.config.ts +16 -0
- package/templates/app/cage.config.json +114 -0
- package/templates/app/compose.yaml +39 -0
- package/templates/app/db/migrations/0001_init.sql +112 -0
- package/templates/app/package.json +25 -0
- package/templates/app/packages/contracts/package.json +16 -0
- package/templates/app/packages/contracts/src/dept.ts +74 -0
- package/templates/app/packages/contracts/src/index.ts +81 -0
- package/templates/app/packages/contracts/src/inventory-lock.ts +52 -0
- package/templates/app/packages/contracts/src/inventory.ts +57 -0
- package/templates/app/packages/contracts/src/purchase-order.ts +72 -0
- package/templates/app/packages/contracts/src/queries.ts +17 -0
- package/templates/app/packages/contracts/src/role.ts +85 -0
- package/templates/app/packages/contracts/src/user.ts +89 -0
- package/templates/app/packages/contracts/tsconfig.json +5 -0
- package/templates/app/packages/domain/package.json +23 -0
- package/templates/app/packages/domain/src/approvals.ts +18 -0
- package/templates/app/packages/domain/src/erp.ts +36 -0
- package/templates/app/packages/domain/src/index.ts +9 -0
- package/templates/app/packages/domain/src/inventory.ts +43 -0
- package/templates/app/packages/domain/src/jobs.ts +13 -0
- package/templates/app/packages/domain/src/purchase-order.test.ts +137 -0
- package/templates/app/packages/domain/src/purchase-order.ts +122 -0
- package/templates/app/packages/domain/src/queries.ts +10 -0
- package/templates/app/packages/domain/src/reactions.ts +24 -0
- package/templates/app/packages/domain/tsconfig.json +5 -0
- package/templates/app/pnpm-workspace.yaml +10 -0
- package/templates/app/renovate.json +5 -0
- package/templates/app/tsconfig.base.json +8 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CAGE-CONN-007
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/connectors/src/runtime.ts:131](../../packages/connectors/src/runtime.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const host = o.connector.hosts[st];
|
|
11
|
+
if (!host) throw new ConnectorError(`[CAGE-CONN-007] 连接器 ${o.connector.name} 没有 ${st} 环境的地址`, null, false);
|
|
12
|
+
const token = o.connector.secret ? await secrets.get(o.connector.secret) : undefined;
|
|
13
|
+
await take(o.connector);
|
|
14
|
+
const url = new URL(o.path, `https://${host}`);
|
|
15
|
+
if (o.method === 'GET') for (const [k, v] of Object.entries(body)) url.searchParams.set(k, String(v));
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
核对 host 白名单、操作名、字段 schema 和签名。永久失败应先修正配置再重放死信。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
hosts: { test: 'erp-sandbox.example.com', prod: 'erp.example.com' }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
// outbound 映射发送了 request schema 之外的字段
|
|
32
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CAGE-CONN-008
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/connectors/src/runtime.ts:151](../../packages/connectors/src/runtime.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
await recordCall(opts.kernel, o, key, opts.tenantId ?? null, 'failed', null, body, undefined, String(e));
|
|
11
|
+
throw new ConnectorError(`[CAGE-CONN-008] 调用 ${o.connector.name}.${o.name} 失败:${String(e)}`, null, true);
|
|
12
|
+
}
|
|
13
|
+
const text = await res.text();
|
|
14
|
+
let json: unknown = null;
|
|
15
|
+
try {
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
核对 host 白名单、操作名、字段 schema 和签名。永久失败应先修正配置再重放死信。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
hosts: { test: 'erp-sandbox.example.com', prod: 'erp.example.com' }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
// outbound 映射发送了 request schema 之外的字段
|
|
32
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CAGE-CONN-009
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/connectors/src/runtime.ts:163](../../packages/connectors/src/runtime.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const retryable = o.effect === 'retryableWrite' || o.effect === 'read' ? res.status >= 500 || res.status === 429 : false;
|
|
11
|
+
throw new ConnectorError(`[CAGE-CONN-009] ${o.connector.name}.${o.name} 返回 ${res.status}`, res.status, retryable);
|
|
12
|
+
}
|
|
13
|
+
const parsed = o.response.safeParse(json);
|
|
14
|
+
if (!parsed.success) {
|
|
15
|
+
await recordCall(opts.kernel, o, key, opts.tenantId ?? null, 'failed', res.status, body, json, '响应格式不符合声明');
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
核对 host 白名单、操作名、字段 schema 和签名。永久失败应先修正配置再重放死信。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
hosts: { test: 'erp-sandbox.example.com', prod: 'erp.example.com' }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
// outbound 映射发送了 request schema 之外的字段
|
|
32
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CAGE-CONN-010
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/connectors/src/runtime.ts:168](../../packages/connectors/src/runtime.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
await recordCall(opts.kernel, o, key, opts.tenantId ?? null, 'failed', res.status, body, json, '响应格式不符合声明');
|
|
11
|
+
throw new ConnectorError(`[CAGE-CONN-010] ${o.connector.name}.${o.name} 的响应格式与声明不符`, res.status, false);
|
|
12
|
+
}
|
|
13
|
+
await recordCall(opts.kernel, o, key, opts.tenantId ?? null, 'ok', res.status, body, parsed.data);
|
|
14
|
+
return parsed.data;
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
核对 host 白名单、操作名、字段 schema 和签名。永久失败应先修正配置再重放死信。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
hosts: { test: 'erp-sandbox.example.com', prod: 'erp.example.com' }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
// outbound 映射发送了 request schema 之外的字段
|
|
32
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CAGE-CONN-011
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/connectors/src/runtime.ts:190](../../packages/connectors/src/runtime.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// 不可重试:标成永久错误,直接进死信
|
|
11
|
+
throw Object.assign(new Error(`[CAGE-CONN-011] ${e.message}`), { permanent: true });
|
|
12
|
+
}
|
|
13
|
+
throw e;
|
|
14
|
+
}
|
|
15
|
+
if (ob.onSuccess) {
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
核对 host 白名单、操作名、字段 schema 和签名。永久失败应先修正配置再重放死信。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
hosts: { test: 'erp-sandbox.example.com', prod: 'erp.example.com' }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
// outbound 映射发送了 request schema 之外的字段
|
|
32
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CAGE-CONN-012
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/cli/src/rules.ts:157](../../packages/cli/src/rules.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
findings.push({
|
|
11
|
+
code: 'CAGE-CONN-012',
|
|
12
|
+
at: at(c),
|
|
13
|
+
reason: `命令 ${c.id} 只接受外部调用,但没有入站回调或出站映射调用它`,
|
|
14
|
+
fix: '用 inbound(...) 或 outbound(..., { onSuccess }) 调用它',
|
|
15
|
+
severity: 'warning',
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
核对 host 白名单、操作名、字段 schema 和签名。永久失败应先修正配置再重放死信。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
hosts: { test: 'erp-sandbox.example.com', prod: 'erp.example.com' }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
// outbound 映射发送了 request schema 之外的字段
|
|
32
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CAGE-CREATE-001
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/cli/src/create.ts:8](../../packages/cli/src/create.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const root = resolve(target);
|
|
11
|
+
if (existsSync(root)) throw new Error('[CAGE-CREATE-001] 目标路径已存在,请选择新目录');
|
|
12
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const template = [resolve(here, '../templates/app'), resolve(here, '../../../templates/app')].find(
|
|
14
|
+
existsSync,
|
|
15
|
+
);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
选择尚不存在的目标目录,并确认 CLI 安装包包含 templates/app。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
cage create new-app
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
cage create existing-project // 拒绝覆盖已有项目
|
|
32
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CAGE-DECL-001
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/cli/src/static-check.ts:188](../../packages/cli/src/static-check.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
findings.push({
|
|
11
|
+
code: 'CAGE-DECL-001',
|
|
12
|
+
at: loc(node),
|
|
13
|
+
reason: `原语 ${name}() 写在函数、条件或循环里,抽取器看不到它`,
|
|
14
|
+
fix: '原语必须在模块顶层用 export const 声明',
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
将声明放在模块顶层,用唯一的 export const 名称声明,并从 domain/index.ts 导出。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
export const Order = aggregate('Order', definition);
|
|
26
|
+
// index.ts
|
|
27
|
+
export * from './order.js';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 反例
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
function build() { return aggregate('Order', definition); }
|
|
34
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CAGE-DECL-002
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/cli/src/static-check.ts:254](../../packages/cli/src/static-check.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
.map((s) => ({
|
|
11
|
+
code: 'CAGE-DECL-002',
|
|
12
|
+
at: `${s.file}:${s.line}`,
|
|
13
|
+
reason: `声明 ${s.exportName} 没有从 domain 入口导出,内核和抽取器都看不到它`,
|
|
14
|
+
fix: `在 domain 的 index.ts 里加上 export * from './${s.file
|
|
15
|
+
.split('/')
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
将声明放在模块顶层,用唯一的 export const 名称声明,并从 domain/index.ts 导出。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
export const Order = aggregate('Order', definition);
|
|
26
|
+
// index.ts
|
|
27
|
+
export * from './order.js';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 反例
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
function build() { return aggregate('Order', definition); }
|
|
34
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CAGE-DECL-003
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/cli/src/static-check.ts:207](../../packages/cli/src/static-check.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
findings.push({
|
|
11
|
+
code: 'CAGE-DECL-003',
|
|
12
|
+
at: loc(node),
|
|
13
|
+
reason: `声明 ${topDecl.name.getText()} 没有用 export const 导出`,
|
|
14
|
+
fix: '加上 export const,并确认它从 domain 的 index.ts 导出',
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
将声明放在模块顶层,用唯一的 export const 名称声明,并从 domain/index.ts 导出。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
export const Order = aggregate('Order', definition);
|
|
26
|
+
// index.ts
|
|
27
|
+
export * from './order.js';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 反例
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
function build() { return aggregate('Order', definition); }
|
|
34
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CAGE-DECL-004
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/cli/src/static-check.ts:239](../../packages/cli/src/static-check.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
findings.push({
|
|
11
|
+
code: 'CAGE-DECL-004',
|
|
12
|
+
at: `${list[0].file}:${list[0].line}`,
|
|
13
|
+
reason: `导出名 ${n} 在 ${list.map((s) => s.file).join('、')} 里重复,export * 会把它们都丢掉`,
|
|
14
|
+
fix: '改成不重复的导出名;命令可以用 command(X, { name: "原名" }) 保持能力点不变',
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
将声明放在模块顶层,用唯一的 export const 名称声明,并从 domain/index.ts 导出。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
export const Order = aggregate('Order', definition);
|
|
26
|
+
// index.ts
|
|
27
|
+
export * from './order.js';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 反例
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
function build() { return aggregate('Order', definition); }
|
|
34
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CAGE-DEP-001
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/cli/src/static-check.ts:144](../../packages/cli/src/static-check.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
findings.push({
|
|
11
|
+
code: 'CAGE-DEP-001',
|
|
12
|
+
at: loc(node),
|
|
13
|
+
reason: `声明文件导入了 IO 模块 ${spec}`,
|
|
14
|
+
fix: '需要外部数据就声明 reads;需要通知外部系统就发事件,由连接器的出站映射处理',
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
移除业务层的 IO 或越层依赖。读取放进 reads,外部调用放进 outbound,前端依赖 contracts。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
reads: { stock: Inventory }
|
|
26
|
+
// handler 中
|
|
27
|
+
await deps.stock.get(id);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 反例
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
import pg from 'pg'; // domain 中禁止直接访问数据库
|
|
34
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CAGE-DEP-002
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/lint/dependency-cruiser.cjs:17](../../packages/lint/dependency-cruiser.cjs)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
severity: 'error',
|
|
11
|
+
comment: '[CAGE-DEP-002] 适配器必须通过命令网关写数据',
|
|
12
|
+
from: { path: '^packages/adapters' },
|
|
13
|
+
to: { path: 'node_modules/(pg|kysely)/' },
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
移除业务层的 IO 或越层依赖。读取放进 reads,外部调用放进 outbound,前端依赖 contracts。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
reads: { stock: Inventory }
|
|
26
|
+
// handler 中
|
|
27
|
+
await deps.stock.get(id);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 反例
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
import pg from 'pg'; // domain 中禁止直接访问数据库
|
|
34
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CAGE-DEP-003
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/lint/dependency-cruiser.cjs:24](../../packages/lint/dependency-cruiser.cjs)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
severity: 'error',
|
|
11
|
+
comment: '[CAGE-DEP-003] 前端只能依赖 @app/contracts',
|
|
12
|
+
from: { path: '^apps/web' },
|
|
13
|
+
to: { path: '^packages/domain|node_modules/@yss123/cage-(kernel|http|admin|connectors|approval|testkit)/' },
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
移除业务层的 IO 或越层依赖。读取放进 reads,外部调用放进 outbound,前端依赖 contracts。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
reads: { stock: Inventory }
|
|
26
|
+
// handler 中
|
|
27
|
+
await deps.stock.get(id);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 反例
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
import pg from 'pg'; // domain 中禁止直接访问数据库
|
|
34
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CAGE-DEP-004
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/cli/src/static-check.ts:155](../../packages/cli/src/static-check.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
findings.push({
|
|
11
|
+
code: 'CAGE-DEP-004',
|
|
12
|
+
at: loc(node),
|
|
13
|
+
reason: name === 'fetch' ? 'handler 里直接发网络请求' : '声明文件里使用了动态 import',
|
|
14
|
+
fix: '第三方调用改为连接器声明 + outbound 出站映射',
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
移除业务层的 IO 或越层依赖。读取放进 reads,外部调用放进 outbound,前端依赖 contracts。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
reads: { stock: Inventory }
|
|
26
|
+
// handler 中
|
|
27
|
+
await deps.stock.get(id);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 反例
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
import pg from 'pg'; // domain 中禁止直接访问数据库
|
|
34
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CAGE-DEP-005
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/lint/dependency-cruiser.cjs:31](../../packages/lint/dependency-cruiser.cjs)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
severity: 'error',
|
|
11
|
+
comment: '[CAGE-DEP-005] domain 不能依赖生成物',
|
|
12
|
+
from: { path: '^packages/domain' },
|
|
13
|
+
to: { path: '^(generated|packages/contracts)/' },
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
移除业务层的 IO 或越层依赖。读取放进 reads,外部调用放进 outbound,前端依赖 contracts。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
reads: { stock: Inventory }
|
|
26
|
+
// handler 中
|
|
27
|
+
await deps.stock.get(id);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 反例
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
import pg from 'pg'; // domain 中禁止直接访问数据库
|
|
34
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CAGE-DLQ-001
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/kernel/src/worker/outbox.ts:128](../../packages/kernel/src/worker/outbox.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const d = await tx.selectFrom('sys.dead_letter').selectAll().where('id', '=', deadLetterId).where('handled_at', 'is', null).forUpdate().executeTakeFirst();
|
|
11
|
+
if (!d) throw new Error('[CAGE-DLQ-001] 死信不存在或已处理');
|
|
12
|
+
await tx.updateTable('sys.outbox').set({ status: 'pending', attempts: 0, next_at: sql`now()`, last_error: null }).where('id', '=', d.outbox_id).execute();
|
|
13
|
+
await tx.updateTable('sys.dead_letter').set({ handled_by: handledBy, handled_at: sql`now()`, resolution: 'replayed' }).where('id', '=', deadLetterId).execute();
|
|
14
|
+
await sql`select pg_notify('cage_outbox', '')`.execute(tx);
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
检查死信详情和事件顺序,修复原因后用管理接口重放或丢弃,不直接改业务表。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
在 /admin 的死信列表检查错误并重放
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
直接更新 sys.outbox 绕过死信处理流程
|
|
32
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CAGE-ENV-001
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/cli/src/migrate.ts:51](../../packages/cli/src/migrate.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const urls = urlsFromEnv(env);
|
|
11
|
+
if (!urls.migrate) throw new Error('[CAGE-ENV-001] 设置 CAGE_MIGRATE_DATABASE_URL');
|
|
12
|
+
if (init) {
|
|
13
|
+
if (!env.CAGE_SUPER_DATABASE_URL) throw new Error('[CAGE-ENV-001] 初始化需要 CAGE_SUPER_DATABASE_URL');
|
|
14
|
+
const password = (url: string) => {
|
|
15
|
+
const pw = decodeURIComponent(new URL(url).password);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
在运行环境设置 app/read/migrate 三种连接串,检查连接账号与库名。不要把凭证写进源码。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
CAGE_APP_DATABASE_URL / CAGE_READ_DATABASE_URL / CAGE_MIGRATE_DATABASE_URL
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
// 未配置数据库连接就启动 API
|
|
32
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CAGE-EVENT-001
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/kernel/src/kernel.ts:365](../../packages/kernel/src/kernel.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
if (!p.success) {
|
|
11
|
+
throw new Error(`[CAGE-EVENT-001] 事件 ${ev.name} 的载荷不符合声明:${JSON.stringify(zodIssues(p.error))}`);
|
|
12
|
+
}
|
|
13
|
+
const payload = maskEventPayload(ev, p.data as Record<string, unknown>);
|
|
14
|
+
const row = await tx
|
|
15
|
+
.insertInto('sys.outbox')
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
保留旧事件字段及类型,新加字段设为 optional;不兼容含义使用新事件名。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
event("OrderChanged", { orderId: id(), note: text().optional() })
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
// 给已有事件新增必填 note,旧 outbox 消息无法提供它
|
|
32
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CAGE-EVENT-002
|
|
2
|
+
|
|
3
|
+
## 什么时候出现
|
|
4
|
+
|
|
5
|
+
命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
|
|
6
|
+
|
|
7
|
+
来源:[packages/cli/src/rules.ts:225](../../packages/cli/src/rules.ts)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
|
|
11
|
+
/** CAGE-EVENT-002:事件载荷只允许向后兼容的变更(只能新增可选字段) */
|
|
12
|
+
export function checkEventCompat(base: Ontology, head: Ontology): Finding[] {
|
|
13
|
+
const findings: Finding[] = [];
|
|
14
|
+
const b = new Map(base.nodes.filter((n) => n.kind === 'event').map((n) => [n.id, n]));
|
|
15
|
+
for (const ev of head.nodes.filter((n) => n.kind === 'event')) {
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 怎么修
|
|
19
|
+
|
|
20
|
+
保留旧事件字段及类型,新加字段设为 optional;不兼容含义使用新事件名。
|
|
21
|
+
|
|
22
|
+
## 正例
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
event("OrderChanged", { orderId: id(), note: text().optional() })
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 反例
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
// 给已有事件新增必填 note,旧 outbox 消息无法提供它
|
|
32
|
+
```
|